DEPENDENT VARIABLE
In this section, we investigate the dependent variable prior to any
filtering.
Our dependent variable is the number of permits issued per census
tract. The unit of analysis is permits per census tract because of
census data availability and the fact that federal and state funding
often flows to areas via census tract or zip code eligibility. We time
and or space to see if there there are any patterns to missing permit
data so as to better determine which years to test and train on.
# See if there are any patterns to missing spatial data
res_permits %>%
group_by(year = year(as.Date(permitissuedate))) %>%
summarise_all(funs(sum(is.na(.)))) %>%
datatable(options = list(pageLength = 12),
style = "auto")
When looking at council district vs. year distribution of permit
data, there are plenty of permits in each district, but wide differences
by year. Permitting activity in earlier years is much lower than 2020 to
2023. Although 2020 is typically considered an anomaly, permit issuance
and development was strong due to low interest rates. Therefore, we
trained 2020 and 2021 permit issuance data to test on 2022 development
activity. As a result, we got rid of missing spatial data
res_permits %>%
group_by(council_district) %>%
summarise(count = n()) %>%
ggplot(aes(y = as.factor(council_district), x = count)) +
geom_bar(stat = "identity", fill = "#ffb600") +
plotTheme() +
labs(title = "Permits by Council District", caption = "Figure 1")

res_permits %>%
group_by(year = year(as.Date(permitissuedate))) %>%
st_drop_geometry() %>%
summarise(count = n()) %>%
ggplot(aes (x = year, y = count)) +
geom_bar(stat = "identity", fill = "#ffb600") +
plotTheme() +
labs(title = "Permits by Year", caption = "Figure 2")

res_permits <- res_permits %>%
filter(!is.na(lng) & !is.na(lat)) %>%
st_as_sf(coords = c("lng","lat"),crs=4326)
Next, we examined permit types and counts to understand the
distribution of permit types specific to Philadelphia. When looking at
the count of permit types, as represented below by n, we can see that
the permit type alone does not indicate what type of work is being done.
New construction will necessitate different permit types, such as
plumbing, mechanical, or electrical permits, but these can also be
counted in regular maintenance. Therefore, filtering only based on new
construction may be misleading. We looked at census data next to begin
understanding how permit data is associated with demographics and our
gentrification indicator.
res_permits %>%
group_by(permitdescription, typeofwork) %>%
st_drop_geometry() %>%
summarise(n = n()) %>%
arrange(permitdescription, desc(n)) %>%
datatable(options = list(pageLength = 10),
style = "auto")
phl_tracts4326 <- tracts(state = "PA",
county = "Philadelphia",
year = "2020") %>%
select(GEOID, geometry) %>%
st_set_crs(4326)
res_permits2 <- res_permits %>%
group_by(permitdescription, typeofwork) %>%
mutate(n = n()) %>%
ungroup() %>%
select(permittype, permitdescription, typeofwork, geometry, permitissuedate) %>%
#join to census tract
st_join(phl_tracts4326)
When we look at number of permits by type per tract as seen below, it
becomes apparent there are a very high concentration of all permits in
the Fishtown, Port Richmond area. When we look by year, we see that some
of this is because of data unavailability in earlier years followed by a
flux of permit issuance from 2020 to 2022.
So what happened these years?
Interest rates were at a historical low in 2020 to 2022, in part
to entice economic activity to be as enticing as possible after the
COVID-19 pandemic brought the economy to a screeching halt. 
More locally, Philadelphia passed a tax policy in 2000 to provide
a tax abatement on new housing construction until it
expired in 2022. The policy goal was to spur development for
residential property owners by providing a 10-year tax abatement on the
value of improvements. This partially explains the time lag of some
development, but it remains unclear why the development was so
concentrated spatially.
Our next data analysis step is to drop the year and look only at
permits in and after 2018. It is likely that more permits are issued in
areas where these changes are already happening.
# PICK 1
tract_permits_year <- res_permits2 %>%
st_drop_geometry() %>%
mutate(permit = paste(permitdescription, "-", typeofwork),
year = year(as.Date(permitissuedate))) %>%
select(-permitissuedate, -permitdescription, -permittype, -typeofwork) %>%
gather(Variable, value, -GEOID, -year) %>%
dplyr::count(Variable, value, GEOID, year) %>%
group_by(GEOID, year) %>%
mutate(TOTAL = sum(n)) %>%
ungroup() %>%
spread(key = value, value = n) %>%
select(-Variable) %>%
replace(is.na(.), 0)
#total permits
tract_permits_year %>%
left_join(phl_tracts4326) %>%
st_sf() %>%
filter(year > 2014 & year < 2023) %>%
ggplot()+
geom_sf(aes(fill = cut(TOTAL, breaks = 8))) +
scale_fill_manual(values = palette1) +
mapTheme() +
labs(title = "Permits between 2014 - 2023", caption = "Figure 3")

#by year - there are a lot of tracts missing in earlier years
# tract_permits_year %>%
# left_join(phl_tracts4326) %>%
# st_sf() %>%
# filter(year > 2014 & year <= 2023) %>%
# ggplot()+
# geom_sf(aes(fill = TOTAL)) +
# scale_fill_binned(n.breaks=8) +
# facet_wrap(~year) +
# mapTheme()
tract_permits <- res_permits2 %>%
st_drop_geometry() %>%
mutate(permit = paste(permitdescription, "-", typeofwork),
year = year(as.Date(permitissuedate))) %>%
filter(year >= 2018) %>%
select(-permitissuedate, -permitdescription, -permittype, -typeofwork, -year) %>%
gather(Variable, value, -GEOID) %>%
dplyr::count(Variable, value, GEOID) %>%
group_by(GEOID) %>%
mutate(TOTAL = sum(n)) %>%
ungroup() %>%
spread(key = value, value = n) %>%
select(-Variable) %>%
replace(is.na(.), 0)
PERMIT SELECTION
Census Data
But a key consideration is: does
demand lead supply or vice versa? Which comes first, development or
gentrification?
There are two theories of neighborhood change as it relates to this.
- 1) Housing demand of middle-class, mostly white residents encourages
them to leave neighborhoods to seek out more affordable neighborhoods. -
2) Development supply of new construction in historically poorer
neighborhoods encourages an influx of new residents, who are encouraged
to move to a neighborhood by the supply of housing.
The above study found that housing market growth (i.e. new permit
issuance) did not predict future increase in gentrification; rather,
development followed neighborhood change. Gentrification features
themselves could be considered indicators of future development.
As a result of permit analysis, we selected permits that were
associated with gentrification indicators. Using prior
research on gentrification indicators and using publicly available
census data, we examined the following demographic characteristics:
- All Census tracts in Philadelphia, 5Y ACS for 2021 and for 2016
- Median HH income, CPI-inflation adjusted
- Median home value, CPI-inflation adjusted
- Median rent, CPI-inflation adjusted
- Median year structure built
- Tenure - owner-occupied
- Number of housing units
- Number of vacancies
- Count of second mortgages
- Percent white population
- Percent with Bachelor’s degree
We calculated: rent to income ratio, net changes of multiple
indicators from 2017 to 2022, and percent changes from 2017 to 2022.
Median rents, values, and incomes were adjusted to 2022 dollars.
CPI2017 <- 242.839
CPI2022 <- 281.148
CPI <- CPI2022/CPI2017
# NOTE: currently replacing NA values with a very small value...
phlCensus22 <-
get_acs(geography = "tract",
variables = c("B01003_001", "B19013_001",
"B02001_002", "B25077_001",
"B25064_001", "B25035_001",
"B25003_001", "B25003_002",
"B25001_001", "B25002_001",
"B25002_003", "B25081_001",
"B25081_004", "B15003_001",
"B15003_022"
),
year = 2022,
dataset = "acs5",
state = "PA",
geometry = FALSE,
county = c("Philadelphia"),
output = "wide") %>%
rename(Total_Pop = B01003_001E,
Med_Inc = B19013_001E,
White_Pop = B02001_002E,
Med_Value = B25077_001E,
Med_Rent = B25064_001E,
Med_Structure = B25035_001E,
Owner_univ = B25003_001E,
Owners = B25003_002E,
Tot_Units = B25001_001E,
Vac_univ = B25002_001E,
Vacants = B25002_003E,
Mort_status_univ = B25081_001E,
Sec_Mort = B25081_004E,
Bach_univ = B15003_001E,
Bach = B15003_022E
) %>%
dplyr::select(Total_Pop, Med_Inc, White_Pop,
Med_Value, Med_Rent, Med_Structure,
Owner_univ, Owners, Tot_Units,
Vac_univ, Vacants, Mort_status_univ,
Sec_Mort, Bach_univ, Bach, GEOID) %>%
mutate(Percent_White = White_Pop / Total_Pop,
Percent_Owner = Owners / Owner_univ,
Percent_Vacant = Vacants / Vac_univ,
Percent_2mort = Sec_Mort / Mort_status_univ,
Percent_bach = Bach / Bach_univ,
Rent_Income_Ratio = Med_Rent / (Med_Inc/12)
) %>%
#replace(is.na(.), .00000001) %>%
select(-ends_with("_univ"))
phlCensus17 <-
get_acs(geography = "tract",
variables = c("B01003_001", "B19013_001",
"B02001_002", "B25077_001",
"B25064_001", "B25035_001",
"B25003_001", "B25003_002",
"B25001_001", "B25002_001",
"B25002_003", "B25081_001",
"B25081_004", "B15003_001",
"B15003_022"
),
year = 2017,
state = "PA",
geometry = FALSE,
county=c("Philadelphia"),
output = "wide") %>%
rename(Total_Pop = B01003_001E,
Med_Inc = B19013_001E,
White_Pop = B02001_002E,
Med_Value = B25077_001E,
Med_Rent = B25064_001E,
Med_Structure = B25035_001E,
Owner_univ = B25003_001E,
Owners = B25003_002E,
Tot_Units = B25001_001E,
Vac_univ = B25002_001E,
Vacants = B25002_003E,
Mort_status_univ = B25081_001E,
Sec_Mort = B25081_004E,
Bach_univ = B15003_001E,
Bach = B15003_022E
) %>%
dplyr::select(Total_Pop, Med_Inc, White_Pop,
Med_Value, Med_Rent, Med_Structure,
Owner_univ, Owners, Tot_Units,
Vac_univ, Vacants, Mort_status_univ,
Sec_Mort, Bach_univ, Bach, GEOID) %>%
mutate(
Med_Rent = Med_Rent * CPI,
Med_Value = Med_Value * CPI,
Percent_White = White_Pop / Total_Pop,
Percent_Owner = Owners / Owner_univ,
Percent_Vacant = Vacants / Vac_univ,
Percent_2mort = Sec_Mort / Mort_status_univ,
Percent_bach = Bach / Bach_univ,
Rent_Income_Ratio = Med_Rent / (Med_Inc/12),
) %>%
#replace(is.na(.), .00000001) %>%
select(-ends_with("_univ"))
# Interpolate based on population in order to translate 2017 tracts to 2020 tracts
# workaround because tidycensus wasn't downloading geometry
#get just population from phlCensus
pop_22 <- phlCensus22 %>%
select(GEOID, Total_Pop)
#get tracts; join population to tracts
phl_tracts22 <- tracts(state = "PA",
county = "Philadelphia",
year = "2022") %>%
left_join(pop_22) %>%
st_sf()
#join tracts to Census data
phlCensus22 <- phlCensus22 %>%
left_join(phl_tracts22 %>%
select(GEOID, geometry)) %>%
st_sf()
#get 2017 tracts
phl_tracts17 <- tracts(state = "PA",
county = "Philadelphia",
year = "2017")
#join tracts to 2017 Census data
phlCensus17 <- phlCensus17 %>%
left_join(phl_tracts17 %>%
select(GEOID,geometry)) %>%
st_sf()
sf_use_s2(FALSE)
census17_interpolate_pw <- interpolate_pw(
phlCensus17,
phlCensus22,
to_id = "GEOID",
extensive = TRUE,
weights = phl_tracts22,
weight_column = "Total_Pop",
crs = 4269)
census_22_and_comparison <- phlCensus22 %>%
left_join(st_drop_geometry(census17_interpolate_pw),
by = "GEOID",
suffix = c("_2022", "_2017")) %>%
mutate(Total_Pop_NET = Total_Pop_2022 - Total_Pop_2017,
Med_Inc_NET = Med_Inc_2022 - Med_Inc_2017,
White_Pop_NET = White_Pop_2022 - White_Pop_2017,
Med_Value_NET = Med_Value_2022 - Med_Value_2017,
Med_Rent_NET = Med_Rent_2022 - Med_Rent_2017,
Owners_NET = Owners_2022 - Owners_2017,
Tot_Units_NET = Tot_Units_2022 - Tot_Units_2017,
Vacants_NET = Vacants_2022 - Vacants_2017,
Sec_Mort_NET = Sec_Mort_2022 - Sec_Mort_2017,
Percent_White_NET = Percent_White_2022 - Percent_White_2017,
Percent_Owner_NET = Percent_Owner_2022 - Percent_Owner_2017,
Percent_Vacant_NET = Percent_Vacant_2022 - Percent_Vacant_2017,
Percent_2mort_NET = Percent_2mort_2022 - Percent_2mort_2017,
Rent_Income_Ratio_NET = Rent_Income_Ratio_2022 - Rent_Income_Ratio_2017,
Bach_NET = Bach_2022 - Bach_2017,
Percent_bach_NET = Percent_bach_2022 - Percent_bach_2017,
Total_Pop_PCT = ifelse(Total_Pop_2017>0, (Total_Pop_NET / Total_Pop_2017), NA)*100,
Med_Inc_PCT = ifelse(Med_Inc_2017 >0, (Med_Inc_NET / Med_Inc_2017), NA)*100,
White_Pop_PCT = ifelse(White_Pop_2017 >0, (White_Pop_NET / White_Pop_2017), NA)*100,
Med_Value_PCT = ifelse(Med_Value_2017>0, (Med_Value_NET / Med_Value_2017), NA)*100,
Med_Rent_PCT = ifelse(Med_Rent_2017>0, (Med_Rent_NET / Med_Rent_2017),NA)*100,
Owners_PCT = ifelse(Owners_2017>0, ( Owners_NET / Owners_2017),NA)*100,
Tot_Units_PCT = ifelse(Tot_Units_2017>0, (Tot_Units_NET / Tot_Units_2017),NA)*100,
Vacants_PCT = ifelse(Vacants_2017>0,(Vacants_NET / Vacants_2017),NA)*100,
Sec_Mort_PCT = ifelse(Sec_Mort_2017>0,(Sec_Mort_NET / Sec_Mort_2017),NA)*100,
Percent_White_PCT =ifelse(Percent_White_2017>0,(Percent_White_NET / Percent_White_2017),NA)*100,
# is it bad to calculate percent change using percentages?
Percent_Owner_PCT = ifelse(Percent_Owner_2017>0,(Percent_Owner_NET / Percent_Owner_2017),NA)*100,
Percent_Vacant_PCT = ifelse(Percent_Vacant_2017>0,(Percent_Vacant_NET / Percent_Vacant_2017),NA)*100,
Percent_2mort_PCT = ifelse(Percent_2mort_2017>0,(Percent_2mort_NET / Percent_2mort_2017),NA)*100,
Rent_Income_Ratio_PCT = ifelse(Rent_Income_Ratio_2017>0, (Rent_Income_Ratio_NET / Rent_Income_Ratio_2017),NA)*100,
Percent_bach_PCT = ifelse(Percent_bach_2017>0, (Percent_bach_NET / Percent_bach_2017),NA)*100) %>%
relocate(GEOID)
Gentrification Indicator
We developed a gentrification indicator methodology based on a
Drexel study. This is just one possible indicator and could be
improved upon or changed by incorporating additional demographic
variables.
These four gentrification categories we use to characterize tracts
are:
- Ineligible
- Not gentrifying
- Gentrifying
- Intensely Gentrifying
We can then see for which characterizations there are differences in
permit types. If the mean number of permits for census tracts that are
gentrifying or intensely gentrifying is larger than for tracts that are
ineligible or not gentrifying, we keep that permit type.
indicator <- census_22_and_comparison %>%
mutate(indicator =
case_when(
# tracts with < 50 people or in top quartile of median income are excluded
Total_Pop_2022 <= 50 | Med_Inc_2017 > quantile(Med_Inc_2017, .75, na.rm = TRUE) ~ "Ineligible",
# tracts with below median increase in pct with bachelor's degree,
# below median changes in rent and home values are not gentrifying
Percent_bach_PCT < median(Percent_bach_NET, na.rm = TRUE) |
(Med_Value_NET < median(Med_Value_NET, na.rm = TRUE) &
Med_Rent_NET < median(Med_Rent_NET, na.rm = TRUE)) ~ "Not gentrifying",
# tracts with above-median percent increases in bachelor degree attainment
# and EITHER b/t .5 and .75 percentile increase in home values or rents = gentrifying
Percent_bach_PCT >= median(Percent_bach_NET, na.rm = TRUE) & (
(Med_Value_NET >= quantile(Med_Value_NET, .50, na.rm = TRUE) &
Med_Value_NET < quantile(Med_Value_NET, .75, na.rm = TRUE)) |
(Med_Rent_NET >= quantile(Med_Rent_NET, .50, na.rm = TRUE) &
Med_Rent_NET < quantile(Med_Rent_NET, .75, na.rm = TRUE))
) ~ "Gentrifying",
# tracts with above-median percent increases in bachelor degree attainment
# and EITHER in .75 percentile increase in home values or rents = intensely gentrifying
Percent_bach_PCT >= median(Percent_bach_NET, na.rm = TRUE) &
(Med_Value_NET >= quantile(Med_Value_NET,.75, na.rm = TRUE) |
Med_Rent_NET >= quantile(Med_Rent_NET,.75, na.rm = TRUE)
)
~ "Intensely gentrifying")
) %>%
select(GEOID, indicator, Total_Pop_2022, Total_Pop_2017,
Med_Inc_2022, Med_Inc_2017,
Percent_bach_NET, Med_Value_NET, Med_Rent_NET)
Permit Selection
We can see below how each permit type relates to each gentrification
indicator, and we can also calculate the net difference and percent
difference between the number of permits in not gentrifying tracts
versus intensely gentrifying tracts.
indicator_table <- indicator %>%
dplyr::select(-Total_Pop_2022, -Total_Pop_2017,
-Med_Inc_2022, -Med_Inc_2017,
-Percent_bach_NET, -Med_Value_NET, -Med_Rent_NET) %>%
left_join(tract_permits) %>%
st_drop_geometry() %>%
select(-GEOID) %>%
gather(Variable, value, -indicator) %>%
group_by(indicator, Variable) %>%
summarise_if(is.numeric, mean, na.rm = TRUE) %>%
ungroup() %>%
spread(key = indicator, value = value) %>%
mutate_if(is.numeric, round, digits = 3) %>%
select(Variable, Ineligible, `Not gentrifying`, Gentrifying, `Intensely gentrifying`)
keep <- indicator_table %>%
mutate(g_to_non_g_PCT = round((Gentrifying - `Not gentrifying`) / `Not gentrifying`, 4) *100,
ig_to_non_g_PCT = round((`Intensely gentrifying` - `Not gentrifying`) / `Not gentrifying`, 4) *100) %>%
filter(Gentrifying >= 1 & `Intensely gentrifying`>=1 & g_to_non_g_PCT > 0 & ig_to_non_g_PCT > 0) %>%
select(-g_to_non_g_PCT, -ig_to_non_g_PCT)
keep %>%
datatable()
keep <- unique(keep$Variable)
Here, we filter only for permits where there is a non-zero percent
difference between intensely gentrifying tracts and non-gentrifying
tracts, and keep only those permits and attach that to census data, so
each tract is assigned an indicator for whether or not it is
gentrifying.
tract_permits_final <- res_permits2 %>%
st_drop_geometry() %>%
mutate(permit = paste(permitdescription, "-", typeofwork),
year = year(as.Date(permitissuedate))) %>%
filter(year >= 2018 & permit %in% keep) %>%
select(-permitissuedate, -permitdescription, -permittype, -typeofwork) %>%
gather(Variable, value, -GEOID, -year) %>%
dplyr::count(Variable, value, GEOID, year) %>%
group_by(GEOID, year) %>%
mutate(TOTAL = sum(n)) %>%
ungroup() %>%
spread(key = value, value = n) %>%
select(GEOID, year, TOTAL) %>%
replace(is.na(.), 0)
census_permits <-
expand.grid(GEOID = unique(tract_permits_final$GEOID),
year=unique(tract_permits_final$year)) %>%
left_join(indicator %>% select(GEOID, indicator)) %>%
left_join(tract_permits_final) %>%
left_join(census_22_and_comparison %>% st_drop_geometry()) %>%
mutate(TOTAL = ifelse(is.na(TOTAL), 0, TOTAL)) %>%
filter(Total_Pop_2022 > 0) %>%
select(-geometry) %>%
left_join(phl_tracts4326) %>%
st_as_sf()
census_permits$indicator <- factor(census_permits$indicator, ordered = TRUE, levels = c("Ineligible", "Not gentrifying", "Gentrifying", "Intensely gentrifying"))
Exploratory Analysis of Filtered Dependent Variable
Now that we have filtered what will ultimately become our dependent
variable, we can see that there are many census tracts where
there are no permits issued across all years.
ggplot(census_permits) +
geom_histogram(aes(x = TOTAL), bins = 40, fill = "#ffb600") +
labs(title = "Permit Distribution", caption = "Figure 4")

There was a huge spike of permits issued in 2020 citywide, likely
spurred by the announced rollback
of the tax abatement period and the historically low interest rates.
As a result, 2021
was a major permit boom year and December
2021 experienced the the highest count of new construction permits
issued since January 2016. The boom
continued in 2022 and news articles throughout the years commented
on the “absurd”
number of new consruction permits issued in Philadelphia.
census_permits %>%
group_by(year) %>%
summarise(TOTAL = sum(TOTAL)) %>%
filter(!is.na(year)) %>%
st_drop_geometry() %>%
ggplot(aes (x = year, y = TOTAL)) +
geom_bar(stat = "identity", fill = "#ffb600") +
plotTheme() +
labs(title = "Permits by Year", caption = "Figure 5")

National and city-wide policies may be influencing development on a
city scale. Gentrification indicators provide a clue as to which tracts
are experiencing change, or very rapid change, if at all. But we haven’t
yet honed in on neighborhoods to guide our feature engineering. We
looked at permits by neighborhood for some clues and could immediately
see that there were high permits issued for neighborhoods that have been
the subject of locla articles about gentrification, including Fishtown,
Point
Breeze, and Lower
Kensington
res_permits3 <- res_permits %>%
group_by(permitdescription, typeofwork) %>%
mutate(n = n()) %>%
ungroup() %>%
select(permittype, permitdescription, typeofwork, geometry, permitissuedate) %>%
st_join(phl.nh) %>%
mutate(permit = paste(permitdescription, "-", typeofwork),
year = year(as.Date(permitissuedate))) %>%
filter(year>=2018 & permit %in% keep)
res_permits3 <- res_permits3 %>%
st_drop_geometry() %>%
group_by(mapname, year) %>%
summarise(n = n()) %>%
arrange(desc(n))
res_permits3 %>%
datatable()
When we look at this over time, we can see clearly that the
neighborhood patterns remain the same, with the same neighborhoods
receiving permits across a 6-year period.
res_permits3 <- res_permits3 %>%
left_join(phl.nh)
res_permits3 %>%
ungroup() %>%
st_as_sf() %>%
ggplot() +
geom_sf(aes(fill = cut(n, breaks = 7))) +
scale_fill_manual(values = palette1) +
facet_wrap(~year) +
mapTheme() +
labs(title = "Permits by Year, Philadelphia", caption = "Figure 6")

Now that we have explored gentrification indicators across
Philadelphia neighborhoods and tracts, permits issued over time and
space, and some basic demographic characteristics, we developed a
composite map where we can see which neighborhoods have gentrification
indicators and are seeing a lot of new development for a given year.
Click through the years to look at how gentrification indicators and
permits change over time.
In 2021, there are area that are gentrifying but not seeing a lot of
new development - particularly in the Northeast and Northwest.
Predicting development is therefore going to be slightly different than
predicting changes in rents, home values, and other demographic
characteristics like racial makeup.
bins = c(0,5,30,50,100,200,300,Inf)
permits18 <- colorBin(
palette = "viridis",
bins = bins,
domain = census_permits %>% filter(year == 2018) %>% .$TOTAL)
permits19 <- colorBin(
palette = "viridis",
bins = bins,
domain = census_permits %>% filter(year == 2019) %>% .$TOTAL)
permits20 <- colorBin(
palette = "viridis",
bins = bins,
domain = census_permits %>% filter(year == 2020) %>% .$TOTAL)
permits21 <- colorBin(
palette = "viridis",
bins = bins,
domain = census_permits %>% filter(year == 2021) %>% .$TOTAL)
permits22 <- colorBin(
palette = "viridis",
bins = bins,
domain = census_permits %>% filter(year == 2022) %>% .$TOTAL)
permits23 <- colorBin(
palette = "viridis",
bins = bins,
domain = census_permits %>% filter(year == 2023) %>% .$TOTAL)
indicator_pal <- colorFactor(palette = c("#c191d9", "#6d5480","#ff66f0", "#fccd32"),
domain = census_permits$indicator)
permits_map <- leaflet() %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addPolygons(data = census_permits %>% filter(year == 2023),
stroke = TRUE,
color = ~indicator_pal(indicator),
weight = 2,
opacity = 1,
fillOpacity = .5,
fillColor = ~permits23(TOTAL),
label = paste("Total Permits:", census_permits %>% filter(year == 2023) %>% .$TOTAL),
group = "2023") %>%
addPolygons(data = census_permits %>% filter(year == 2022),
stroke = TRUE,
color = ~indicator_pal(indicator),
weight = 2,
opacity = 1,
fillOpacity = .5,
fillColor = ~permits22(TOTAL),
label = paste("Total Permits:", census_permits %>% filter(year == 2022) %>% .$TOTAL),
group = "2022") %>%
addPolygons(data = census_permits %>% filter(year == 2021),
stroke = TRUE,
color = ~indicator_pal(indicator),
weight = 2,
opacity = 1,
fillOpacity = .5,
fillColor = ~permits21(TOTAL),
label = paste("Total Permits:", census_permits %>% filter(year == 2021) %>% .$TOTAL),
group = "2021") %>%
addPolygons(data = census_permits %>% filter(year == 2020),
stroke = TRUE,
color = ~indicator_pal(indicator),
weight = 2,
opacity = 1,
fillOpacity = .5,
fillColor = ~permits20(TOTAL),
label = paste("Total Permits:", census_permits %>% filter(year == 2020) %>% .$TOTAL),
group = "2020") %>%
addPolygons(data = census_permits %>% filter(year == 2019),
stroke = TRUE,
color = ~indicator_pal(indicator),
weight = 2,
opacity = 1,
fillOpacity = .5,
fillColor = ~permits19(TOTAL),
label = paste("Total Permits:", census_permits %>% filter(year == 2019) %>% .$TOTAL),
group = "2019") %>%
addPolygons(data = census_permits %>% filter(year == 2018),
stroke = TRUE,
color = ~indicator_pal(indicator),
weight = 2,
opacity = 1,
fillOpacity = .5,
fillColor = ~permits18(TOTAL),
group = "2018",
label = paste("Total Permits:", census_permits %>% filter(year == 2018) %>% .$TOTAL)) %>%
addLegend(data = census_permits,
pal = indicator_pal,
value = ~indicator,
title = "Gentrification Indicator",
position = "bottomleft") %>%
addLayersControl(
baseGroups = c("2023", "2022", "2021", "2020", "2019", "2018"),
options = layersControlOptions(collapsed = FALSE))
permits_map %>%
setView("-75.139446","39.996849",zoom = 11)
When we just look at our gentrification indicator alone (without
permits), we can see that not all development is a result of
gentrification, but there are still some areas where the two overlap.
Although this project aims to provide targeted areas for Housing Trust
Funds, we have to recognize that permits alone do not perfectly align
with gentrification, but merely reflect new development.
census_permits %>%
filter(!is.na(year)) %>%
st_sf() %>%
ggplot()+
geom_sf(aes(fill = indicator)) +
scale_fill_manual(values = palette1) +
mapTheme() +
labs(title = "Gentrification Indicators", caption = "Figure 7")

FEATURE ENGINEERING
Spatial and spatial time lags, Census data
Developing spatial and spatial time lags can help us capture the
spatial endogeneity of gentrification as discussed here.
We created a spatial weights matrix based on contiguity relationships of
the census tracts sharing edges and vertices. Specifically, we looked at
the nearest neighbor for prices, rents, and demographics of neighboring
census tracts as possible key variables to predict for permit
development.
We also created a lag for permits issued to last year’s neighbors.
Spatial lag and spatial time lag has major predictive potential for our
modeling efforts. The average permit count for the nearest census tract
can be helpful, but utilizing the nearest neighbor for social
demographic data has the potential to be more influence. As high-demand
neighborhoods become more price unattainable, residents priced out try
to remain close and choose neighborhoods with some adjacent
characteristics such as bachelors degerees, income, or white residents,
and therefore more likelihood of higher social integration.
nb <- st_contiguity(census_permits$geometry, queen = TRUE)
wt <- st_weights(nb)
census_permits <- census_permits %>%
mutate(nn_mean_rent_22 = st_lag(Med_Rent_2022,nb,wt, na_ok = TRUE),
nn_mean_price_22 = st_lag(Med_Value_2022, nb,wt, na_ok = TRUE),
nn_mean_rent_17 = st_lag(Med_Rent_2017,nb,wt, na_ok = TRUE),
nn_mean_price_17 = st_lag(Med_Value_2017, nb,wt, na_ok = TRUE),
nn_bach_22 = st_lag(Percent_bach_2022, nb,wt, na_ok = TRUE),
nn_bach_17 = st_lag(Percent_bach_2017, nb,wt, na_ok = TRUE),
nn_income_22 = st_lag(Med_Inc_2022, nb,wt, na_ok = TRUE),
nn_income_17 = st_lag(Med_Inc_2017, nb,wt, na_ok = TRUE),
nn_pct_white_22 = st_lag(Percent_White_2022, nb,wt, na_ok = TRUE),
nn_pct_white_17 = st_lag(Percent_White_2017, nb,wt, na_ok = TRUE)
)
census_permits <- census_permits %>%
group_by(GEOID) %>%
arrange(GEOID, year) %>%
mutate(last_year_TOTAL = dplyr::lag(TOTAL),
last_year_TOTAL = ifelse(is.na(last_year_TOTAL), TOTAL, last_year_TOTAL)) %>%
ungroup() %>%
mutate(nn_LY_permits = st_lag(last_year_TOTAL, nb, wt))
Local Moran’s I
Moran’s I can help identify spatial autocorrelation, and similarity
among clusters of values. Moran’s I values throughout Philadelphia are
largely above 1, indicating statistically significant clusters wherein
nearest neighbors are having an impact on permit count. Below, we can
see that that most of Philadelphia has very low Moran’s I values except
for Fishtown and University City, which are outliers. Similarly, there
are higher probability values in the areas surrounding Fishtown and
University City, particularly in West Philly.
Higher probability for the nearest neighborhoods surrounding the high
demand neighborhoods suggest that there is some uncertainty regarding
prediction for these tracts. Although this generally indicates that our
confidence for predicting these tracts is lower, the clustering of these
probability values suggests that the nearest neighbor and spatial lag is
indicative of some factor, even if we are not identifying it with
exactness.
nb <- poly2nb(census_permits$geometry, queen=TRUE)
wt <- nb2listw(nb, style="W", zero.policy=TRUE)
local_morans <- localmoran(census_permits$TOTAL, wt, zero.policy=TRUE) %>%
as.data.frame() %>%
rename(c("Local_Morans_I" = "Ii", "P_Value" = "Pr(z != E(Ii))"))
census_permits <-
cbind(census_permits, local_morans %>% select(Local_Morans_I, P_Value)) %>%
st_sf() %>%
mutate(Significant_Hotspots = ifelse(P_Value <= 0.001, 1, 0))
#Plot Morans I
localMorans <-
cbind(local_morans, census_permits) %>%
st_sf() %>%
dplyr::select(TOTAL,
Local_Morans_I,
P_Value) %>%
mutate(Significant_Hotspots = ifelse(P_Value <= 0.001, 1, 0)) %>%
gather(Variable, Value, -geometry)
## This is just for plotting
vars <- unique(localMorans$Variable)
varList <- list()
for(i in vars){
varList[[i]] <-
ggplot() +
geom_sf(data = filter(localMorans, Variable == i),
aes(fill = Value), colour=NA) +
scale_fill_viridis_b(name="") +
labs(title=i) +
mapTheme(title_size = 14) + theme(legend.position="bottom")
}
do.call(grid.arrange,c(varList, ncol = 4, top = "Local Morans I statistics, Total Permits"))

Additional Feature Engineering
Coffee shops
Finally, we generated a few amenity features such as stores or
infrastructure that may make a neighborhood more attractive. These
include cafes from 2018 to 2023 since the presence of new cafes may be
correlated with gentrification or new development.
#coffee shops by year, then join to census_permits.
# cafes18 <- getbb('Philadelphia, PA') %>%
# opq(datetime = '2018-01-01T00:00:01Z') %>%
# add_osm_feature(key = "amenity", value = "cafe") %>%
# osmdata_sf()
#
# cafes18.sf <- st_as_sf(cafes18$osm_points)
#
# cafes18.sf <- cafes18.sf %>%
# st_transform(crs = st_crs(census_permits)) %>%
# st_join(census_permits, join = st_within)
#
# cafes18.sf <- cafes18.sf %>%
# filter(!is.na(GEOID)) %>%
# group_by(GEOID) %>%
# summarise(cafe_count = n()) %>%
# mutate(cafeyear = 2018)
#
# cafes19 <- getbb('Philadelphia, PA') %>%
# opq(datetime = '2019-01-01T00:00:01Z') %>%
# add_osm_feature(key = "amenity", value = "cafe") %>%
# osmdata_sf()
#
# cafes19.sf <- st_as_sf(cafes19$osm_points) %>%
# st_transform(crs = st_crs(census_permits)) %>%
# st_join(census_permits, join = st_within) %>%
# filter(!is.na(GEOID)) %>%
# group_by(GEOID) %>%
# summarise(cafe_count = n()) %>%
# mutate(cafeyear = 2019)
#
# cafes20 <- getbb('Philadelphia, PA') %>%
# opq(datetime = '2020-01-01T00:00:01Z') %>%
# add_osm_feature(key = "amenity", value = "cafe") %>%
# osmdata_sf()
#
# cafes20.sf <- st_as_sf(cafes20$osm_points) %>%
# st_transform(crs = st_crs(census_permits)) %>%
# st_join(census_permits, join = st_within) %>%
# filter(!is.na(GEOID)) %>%
# group_by(GEOID) %>%
# summarise(cafe_count = n()) %>%
# mutate(cafeyear = 2020)
#
# cafes21 <- getbb('Philadelphia, PA') %>%
# opq(datetime = '2021-01-01T00:00:01Z') %>%
# add_osm_feature(key = "amenity", value = "cafe") %>%
# osmdata_sf()
#
# cafes21.sf <- st_as_sf(cafes21$osm_points) %>%
# st_transform(crs = st_crs(census_permits)) %>%
# st_join(census_permits, join = st_within) %>%
# filter(!is.na(GEOID)) %>%
# group_by(GEOID) %>%
# summarise(cafe_count = n()) %>%
# mutate(cafeyear = 2021)
#
# cafes22 <- getbb('Philadelphia, PA') %>%
# opq(datetime = '2022-01-01T00:00:01Z') %>%
# add_osm_feature(key = "amenity", value = "cafe") %>%
# osmdata_sf()
#
# cafes22.sf <- st_as_sf(cafes22$osm_points) %>%
# st_transform(crs = st_crs(census_permits)) %>%
# st_join(census_permits, join = st_within) %>%
# filter(!is.na(GEOID)) %>%
# group_by(GEOID) %>%
# summarise(cafe_count = n()) %>%
# mutate(cafeyear = 2022)
#
# cafes23 <- getbb('Philadelphia, PA') %>%
# opq(datetime = '2023-01-01T00:00:01Z') %>%
# add_osm_feature(key = "amenity", value = "cafe") %>%
# osmdata_sf()
#
# cafes23.sf <- st_as_sf(cafes23$osm_points) %>%
# st_transform(crs = st_crs(census_permits)) %>%
# st_join(census_permits, join = st_within) %>%
# filter(!is.na(GEOID)) %>%
# group_by(GEOID) %>%
# summarise(cafe_count = n()) %>%
# mutate(cafeyear = 2023)
#
# cafes_list <- list(cafes18.sf, cafes19.sf, cafes20.sf, cafes21.sf, cafes22.sf,
# cafes23.sf)
#
# all_cafes <- reduce(cafes_list, bind_rows) %>%
# rename("year" = "cafeyear") %>%
# st_drop_geometry()
#
#
# write.csv(all_cafes, "Data/all_cafes.csv")
#
# rm(cafe18, cafes18.sf, cafes19, cafes19.sf, cafes20, cafes20.sf, cafes21, cafes21.sf, cafes22, cafes22.sf, cafes23, cafes23.sf)
all_cafes <- read.csv("Data/all_cafes.csv") %>%
select(-X)
all_cafes$GEOID <- as.character(all_cafes$GEOID)
census_permits <- census_permits %>%
left_join(all_cafes, by = c("GEOID", "year")) %>%
mutate(cafe_count = ifelse(is.na(cafe_count), 0, cafe_count))
Distance to transit stations
We also looked at proximity to fixed public transportation within the
city of Philadelphia, including regional rail, the Broad Street Line,
Market Frankford Line, and trolley lines.
regionalrail <- st_read("Data/Regional_Rail_Stations.geojson")
trolleys <- st_read("Data/Trolley_Stations.geojson") %>%
rename(Line_Name = LineAbbr,
Latitude = Lat,
Longitude = Lon,
Station_Na = StopName) %>%
select(FID, Line_Name, Station_Na, Latitude, Longitude, geometry)
metro <- st_read("Data/Highspeed_Stations.geojson") %>%
rename(Line_Name = Route,
Station_Na = Station)
transit <- rbind(regionalrail, trolleys, metro) %>%
st_transform(crs = st_crs(phl_tracts22))
phl_transit <- st_join(transit, phl_tracts22, join = st_within) %>%
filter(!is.na(GEOID)) %>%
select(Line_Name, Station_Na, Latitude, Longitude, GEOID, geometry) %>%
st_transform(crs = st_crs(census_permits))
census_permits <- census_permits %>%
mutate(nearest_transit = nn_function(st_coordinates(st_centroid(census_permits)), st_coordinates(phl_transit), k=1)*69.2)
Number of jobs within tract
Finally, we looked at job counts per tract. Although we initially
were going to look at places such as major universities or distance to
center city, ultimately, we recognized that these locations are
substitutes for job density. We looked at jobs over time, but overall
the proportion remains pretty similar across the years for which we have
data. Therefore, we interpolate
the geographies to 2020 tracts and then look at spatial lag – jobs in
surrounding census tracts. This feature could use some further
development, because our visual assessment is that people do not want to
live in the areas with the highest job density, but a within a certain
distance of those job centers.
#use Lehdr package to bring in jobs and to analyze where people are going by work and home census tract.
#derived from: https://github.com/jamgreen/lehdr
#pa jobs from LEHDR
#match with lodes data from previous year, grab data for 2019 - 2022
#match the individual permit sheet with the previous years job data
#
# pa_jobs19 <- grab_lodes(state = "pa",
# year = 2019,
# version = "LODES8",
# lodes_type = "od",
# job_type = "JT01",
# segment = "S000",
# state_part = "main",
# agg_geo = "tract") %>%
# select(-h_tract)
#
# pa_jobs20 <- grab_lodes(state = "pa",
# year = 2020,
# version = "LODES8",
# lodes_type = "od",
# job_type = "JT01",
# segment = "S000",
# state_part = "main",
# agg_geo = "tract")%>%
# select(-h_tract)
#
# pa_jobs21 <- grab_lodes(state = "pa",
# year = 2021,
# version = "LODES8",
# lodes_type = "od",
# job_type = "JT01",
# segment = "S000",
# state_part = "main",
# agg_geo = "tract")%>%
# select(-h_tract)
#
# #filter for only jobs that start or end in Philadelphia tracts
#
# phl_jobs19 <- pa_jobs19 %>%
# left_join(phl_tracts22, by = c("w_tract" = "GEOID")) %>%
# filter(!st_is_empty(geometry)) %>%
# st_as_sf() %>%
# rename(GEOID = w_tract) %>%
# group_by(GEOID) %>%
# summarise(job_count = n())
#
# phl_jobs19 <- interpolate_pw(
# phl_jobs19,
# phl_tracts22,
# to_id = "GEOID",
# extensive = TRUE,
# weights = phl_tracts22,
# weight_column = "Total_Pop",
# crs = 4269)
#
# phl_jobs20 <- pa_jobs20 %>%
# left_join(phl_tracts22, by = c("w_tract" = "GEOID")) %>%
# filter(!st_is_empty(geometry)) %>%
# st_as_sf() %>%
# rename(GEOID = w_tract) %>%
# group_by(GEOID) %>%
# summarise(job_count = n())
#
# phl_jobs21 <- pa_jobs21 %>%
# left_join(phl_tracts22, by = c("w_tract" = "GEOID")) %>%
# filter(!st_is_empty(geometry)) %>%
# st_as_sf() %>%
# rename(GEOID = w_tract) %>%
# group_by(GEOID) %>%
# summarise(job_count = n())
#
# phl_jobs_combined <- rbind(cbind(phl_jobs19, year = 2019),
# cbind(phl_jobs20, year = 2020),
# cbind(phl_jobs21, year = 2021))
#
# ggplot(phl_jobs_combined) +
# geom_sf(aes(fill = job_count)) +
# labs(title = "Job Distribution in Philadelphia", color = "Job Count") +
# mapTheme() +
# facet_wrap(~year, ncol = 3)
#
# phl_jobs_combined$year <- as.Numeric(phl_jobs_combined$year)
phl_jobs_combined <- read.csv("Data/phl_jobs.csv") %>%
select(-X) %>%
mutate(GEOID = as.character(GEOID)) %>%
rename("year_jobs" = "year")
census_permits <- census_permits %>%
mutate(year_jobs = case_when(year == 2018 ~ 2019,
year == 2022 ~ 2021,
year == 2023 ~ 2021,
year %in% c(2019,2020,2021) ~ year)) %>%
left_join(phl_jobs_combined,
by = c("GEOID","year_jobs")) %>%
select(-year_jobs)
phl_jobs_combined <- phl_jobs_combined %>%
left_join(phl_tracts22, by = c("GEOID" = "GEOID")) %>%
select(job_count, GEOID, geometry) %>%
st_as_sf()
nb <- st_contiguity(census_permits$geometry, queen = TRUE)
wt <- st_weights(nb)
census_permits <- census_permits %>%
mutate(jobs.nn= st_lag(job_count,nb,wt, na_ok = TRUE))
Neighborhoods
census_permits <- st_join(st_centroid(census_permits), phl.nh, join = st_within) %>%
st_drop_geometry() %>%
left_join(phl_tracts4326) %>%
st_as_sf()
EXPLORATORY ANALYSIS
Here, we look at our dependent variable, total permits, as a function
of our numeric variables. Last year’s permits and the Local Moran’s I
are most strongly correlated.
Some of these graphs show that permits spike for values in the middle
of the distribution. As an experiment, we could make a binary variable
for relationships where there’s a spike in the middle - between the 45th
and 55th percentile or not.
#filter the outliers
census_permits_test <- census_permits %>%
filter(TOTAL < 600) %>%
dplyr::select(-indicator, -mapname, -name, -Significant_Hotspots) %>%
gather(variable, value, -geometry, -year, -GEOID, -TOTAL)
ggplot(census_permits_test, aes(value, TOTAL)) +
geom_point() +
stat_smooth(aes(value, TOTAL),
method = "lm", se = FALSE, size = 1, colour="#ffb600") +
facet_wrap(~variable, scales= "free", ncol=4) +
labs(title = "Exploratory Analysis", caption = "Figure 8")

We can test this theory out on the variables that appear to have the
largest spikes in the middle. It turns out this is only true for three
variables – the middle percentile (between 45th and 55th percentile) is
related to the number of permits for the change in percentage of homes
with a second mortgage between 2017 and 2022 5-Year ACS surveys, for
2017 adjusted mean rent, and for percent owner in 2022.
census_permits_test <- census_permits %>%
mutate(
middle_bach_17 = ifelse(Bach_2017 > quantile(Bach_2017, .45, na.rm = TRUE) & Bach_2017 <= quantile(Bach_2017, .55, na.rm = TRUE), TRUE, FALSE),
#maybe this one
middle_rent_17 = ifelse(Med_Rent_2017 > quantile(Med_Rent_2017, .45, na.rm = TRUE) & Med_Rent_2017 <= quantile(Med_Rent_2017, .55, na.rm = TRUE), TRUE, FALSE),
middle_price_NET = ifelse(Med_Value_NET > quantile(Med_Value_NET, .45, na.rm = TRUE) & Med_Value_NET <= quantile(Med_Value_NET, .55, na.rm = TRUE), TRUE, FALSE),
middle_nn_bach_22 = ifelse(nn_bach_22 > quantile(nn_bach_22, .45, na.rm = TRUE) & nn_bach_22 <= quantile(nn_bach_22, .55, na.rm = TRUE), TRUE, FALSE),
middle_nn_income_22 = ifelse(nn_income_22 > quantile(nn_income_22, .45, na.rm = TRUE) & nn_income_22 <= quantile(nn_income_22, .55, na.rm = TRUE), TRUE, FALSE),
middle_nn_mean_price_22 = ifelse(nn_mean_price_22 > quantile(nn_mean_price_22, .45, na.rm = TRUE) & nn_mean_price_22 <= quantile(nn_mean_price_22, .55, na.rm = TRUE), TRUE, FALSE),
middle_nn_mean_rent_22 = ifelse(nn_mean_rent_22 > quantile(nn_mean_rent_22, .45, na.rm = TRUE) & nn_mean_rent_22 <= quantile(nn_mean_rent_22, .55, na.rm = TRUE), TRUE, FALSE),
middle_Owners_PCT = ifelse(Owners_PCT > quantile(Owners_PCT, .45, na.rm = TRUE) & Owners_PCT <= quantile(Owners_PCT, .55, na.rm = TRUE), TRUE, FALSE),
#this one
middle_Percent_2mort_NET = ifelse(Percent_2mort_NET > quantile(Percent_2mort_NET, .45, na.rm = TRUE) & Percent_2mort_NET <= quantile(Percent_2mort_NET, .55, na.rm = TRUE), TRUE, FALSE),
middle_Percent_bach_2022 = ifelse(Percent_bach_2022 > quantile(Percent_bach_2022, .45, na.rm = TRUE) & Percent_bach_2022 <= quantile(Percent_bach_2022, .55, na.rm = TRUE), TRUE, FALSE),
middle_Percent_bach_PCT = ifelse(Percent_bach_PCT > quantile(Percent_bach_PCT, .45, na.rm = TRUE) & Percent_bach_PCT <= quantile(Percent_bach_PCT, .55, na.rm = TRUE), TRUE, FALSE),
#this one
middle_Percent_Owner_2022 = ifelse(Percent_Owner_2022 > quantile(Percent_Owner_2022, .45, na.rm = TRUE) & Percent_Owner_2022 <= quantile(Percent_Owner_2022, .55, na.rm = TRUE), TRUE, FALSE),
middle_Rent_Income_Ratio_2017 = ifelse(Rent_Income_Ratio_2017 > quantile(Rent_Income_Ratio_2017, .45, na.rm = TRUE) & Rent_Income_Ratio_2017 <= quantile(Rent_Income_Ratio_2017, .55, na.rm = TRUE), TRUE, FALSE),
#Also testing this new binary variable!
transit_under_1.5 = ifelse(nearest_transit <= 1.5, TRUE, FALSE)
) %>%
dplyr::select(TOTAL, starts_with('middle'), transit_under_1.5) %>%
st_drop_geometry() %>%
gather(variable, value, -TOTAL)
census_permits_test %>%
filter(TOTAL < 600) %>%
group_by(variable, value) %>%
summarise(mean_permits = mean(TOTAL)) %>%
ungroup() %>%
ggplot(aes(value,mean_permits)) +
geom_bar(position = "dodge", stat= "identity", fill = "#ffb600") +
facet_wrap(~variable, scales = "free") +
labs(x="Total Permits", y="Value",
title = "Middle percentile (45-55th) associations with number of permits", caption = "Figure 9") +
theme(legend.position = "none")

# Mutating only for the variables variables that mattered
census_permits <- census_permits %>%
mutate(middle_Percent_Owner_2022 = ifelse(Percent_Owner_2022 > quantile(Percent_Owner_2022, .45, na.rm = TRUE) & Percent_Owner_2022 <= quantile(Percent_Owner_2022, .55, na.rm = TRUE), TRUE, FALSE),
middle_Percent_2mort_NET = ifelse(Percent_2mort_NET > quantile(Percent_2mort_NET, .45, na.rm = TRUE) & Percent_2mort_NET <= quantile(Percent_2mort_NET, .55, na.rm = TRUE), TRUE, FALSE),
middle_rent_17 = ifelse(Med_Rent_2017 > quantile(Med_Rent_2017, .45, na.rm = TRUE) & Med_Rent_2017 <= quantile(Med_Rent_2017, .55, na.rm = TRUE), TRUE, FALSE),
transit_under_1.5 = ifelse(nearest_transit <= 1.5, TRUE, FALSE))
#Mapping cafes and vacancy:
# ggplot(census_permits) +
# geom_sf(aes(fill = cafe_count)) +
# labs(title = "Number of Cafes per Tract") +
# facet_wrap(~year) +
# mapTheme()
#
# ggplot(census_permits %>% filter(year == 2023)) +
# geom_sf(aes(fill = Percent_Vacant_2016)) +
# labs(title = "Vacancy Rate 2016") +
# mapTheme()
Correlation Matrix
We compared variables against each other in a correlation test. Many
of the variables correlated with each other more than with the count of
total permits. The most correlated variables with permit count are
vacancy with a positive correlation, wherein more vacancy means higher
permit issuance. After some additional analysis and feature engineering
however, we realize that close proximity to transit (within 1.5 mile) is
more significant than the nearest neighbor transit. Variables are of
course correlated with each other, but overall census variables are not
as correlated with permit count as expected.
numericVars <- census_permits %>%
st_drop_geometry() %>%
select(TOTAL, cafe_count, nearest_transit, job_count, Med_Inc_2022, Rent_Income_Ratio_NET,
White_Pop_2022, Med_Value_NET, Vacants_2022, Vacants_2017,
Vacants_PCT, Vacants_NET)
price.corr <- corr.test(numericVars)
if (sum(is.na(numericVars)) > 0) {
# Handle missing values (e.g., impute or remove)
numericVars <- na.omit(numericVars) # Remove rows with missing values
}
correlation_matrix <- round(cor(numericVars), 1)
p_values <- cor_pmat(numericVars)
ggcorrplot(
correlation_matrix,
p.mat = p_values,
colors = c( "#f20089", "white", "#2d00f7"),
type = "lower",
insig = "blank"
) +
labs(title = "Degrees of Correlation to Permits Issued", caption = "Figure 10") +
annotate(
geom = "rect",
xmin = .5, xmax = 11.5, ymin = .5, ymax = 1.5,
fill = "transparent", color = "#ffb600", alpha = 0.5
)

MODELING
For the baseline, we included census variables including median HH
income (CPI inflation adjusted), Median home value, Median rent, Median
year structure built, tenure, number of housing units, number of
vacancies, count of second mortgages, percent with a Bachelor’s degree,
and percent white population.
After doing our exploratory analysis, we narrowed down the variables
for our model. For the spatial and temporal lag model, we used these
variables, grouped into the following categories:
TIME/SPACE
- name (neighborhood)
- year
GENTRIFICATION INDICATORS
- indicator
(incorporates bachelors degrees, income price changes, rent changes, and
population)
DEMOGRAPHIC (the strongest)
- Bach_NET
-
middle_Percent_Owner_2022
- middle_Percent_2mort_NET
(other demographic variables) - Med_Value_NET
-
Percent_2mort_2022
- Percent_bach_NET
- Rent_Income_Ratio_2022
- White_Pop_NET
AMENITIES
- cafe_count
- job_count
-
transit_under_1.5
TIME LAG
- last_year_TOTAL
-
Med_Value_2017
- Total_Units_2017
- Vacants_2017
-
middle_rent_17
SPATIAL LAG
- Local Moran’s I
-
nn_LY_permits
- nn_bach_22
- nn_income_17
Several iterations of models were developed over the course of this
project, each attempting to isolate and coalesce variables in different
combinations to create as accurate a prediction as possible.
We used three cross-validation methods: random 10-fold
cross-validation, spatial cross-validation, and temporal
cross-validation. By cross validating, we can detect generalizability
& accuracy for the model’s predictions across time and space in
Philadelphia. Cross validation will also help determine whether our
model is overfitting. With overtfitting, the model is very accurate but
not generalizable to new datasets as new data becomes available and
input into the model.
set.seed(152)
census_permits_dat <- census_permits %>%
filter(year %in% c(2020, 2021, 2022)) %>%
mutate(cvID = sample(round(nrow(.) / 115.5),
size=nrow(.), replace = TRUE)) %>%
filter(name != "BYBERRY" & name != "WISSAHICKON_PARK" & name != "GLENWOOD" & name != "WISTER"
& name != "MCGUIRE")
# BASELINE
reg.baseline.vars <- c("year","indicator","name","Percent_White_2022","Percent_Owner_2022", "Percent_Vacant_2022", "Med_Inc_2022","Med_Value_2022","Med_Rent_2022", "Med_Structure_2022",
"Tot_Units_2022", "Vacants_2022", "Sec_Mort_2022", "Percent_bach_2022",
"Rent_Income_Ratio_2022",
"Bach_NET", "middle_Percent_Owner_2022","middle_Percent_2mort_NET",
"Med_Value_NET","Percent_bach_NET", "White_Pop_NET",
"cafe_count", "job_count", "transit_under_1.5")
reg.baseline.vars.space <- reg.baseline.vars[reg.baseline.vars != "name"]
reg.baseline.vars.time <- reg.baseline.vars[reg.baseline.vars != "year"]
# SPACE AND TIME - zero inflated
reg.st.vars <- c( "year", "indicator", "name","Bach_NET","middle_Percent_Owner_2022","middle_Percent_2mort_NET",
"Med_Value_NET", "Percent_2mort_2022", "Percent_bach_NET",
"Rent_Income_Ratio_2022", "White_Pop_NET", "cafe_count",
"transit_under_1.5", "last_year_TOTAL",
"Med_Value_2017", "Tot_Units_2017", "Vacants_2017",
"middle_rent_17", "Local_Morans_I", "nn_LY_permits",
"nn_bach_22", "nn_income_17", "jobs.nn")
reg.st.space <-reg.st.vars[reg.st.vars != "name"]
reg.st.time <- reg.st.vars[reg.st.vars != "year"]
# Baseline
reg.baseline.random <- crossValidate(
dataset = census_permits_dat,
id = "cvID",
dependentVariable = "TOTAL",
indVariables = reg.baseline.vars) %>%
dplyr::select(cvID = cvID, TOTAL, Prediction, name)
reg.baseline.cv.time <- crossValidate(
dataset = census_permits_dat,
id = "year",
dependentVariable = "TOTAL",
indVariables = reg.baseline.vars.time) %>%
dplyr::select(cvID = year, TOTAL, Prediction, name)
reg.baseline.cv.space <- crossValidate(
dataset = census_permits_dat,
id = "name",
dependentVariable = "TOTAL",
indVariables = reg.baseline.vars.space) %>%
dplyr::select(cvID = name, TOTAL, Prediction) %>%
mutate(name = cvID) %>%
dplyr::select(cvID, TOTAL, Prediction, name, geometry)
# space/time lag
reg.st.cv.random <- crossValidate(
dataset = census_permits_dat,
id = "cvID",
dependentVariable = "TOTAL",
indVariables = reg.st.vars) %>%
dplyr::select(cvID = cvID, TOTAL, Prediction, name)
reg.st.cv.time <- crossValidate(
dataset = census_permits_dat,
id = "year",
dependentVariable = "TOTAL",
indVariables = reg.st.time) %>%
dplyr::select(cvID = year, TOTAL, Prediction, name)
reg.st.cv.space <- crossValidate(
dataset = census_permits_dat,
id = "name",
dependentVariable = "TOTAL",
indVariables = reg.st.space) %>%
dplyr::select(cvID = name, TOTAL, Prediction) %>%
mutate(name = cvID) %>%
dplyr::select(cvID, TOTAL, Prediction, name, geometry)
reg.summary <-
rbind(
mutate(reg.baseline.random, Error = Prediction - TOTAL,
Regression = "Random k-fold CV: Baseline Variables"),
mutate(reg.st.cv.random, Error = Prediction - TOTAL,
Regression = "Random k-fold CV: Spatial and Temporal Lag"),
mutate(reg.baseline.cv.space, Error = Prediction - TOTAL,
Regression = "Spatial LOGO-CV: Baseline Variables"),
mutate(reg.st.cv.space, Error = Prediction - TOTAL,
Regression = "Spatial LOGO-CV: Spatial and Temporal Lag"),
mutate(reg.baseline.cv.time, Error = Prediction - TOTAL,
Regression = "Temporal LOGO-CV: Baseline Variables"),
mutate(reg.st.cv.time, Error = Prediction - TOTAL,
Regression = "Temporal LOGO-CV: Spatial and Temporal Lag")
) %>%
st_sf()
Explore Errors
Mean absolute error is quite low and similar across all cross
validation methods with only a few outliers. These outliers have
significant count differences, but the rest of the neighborhoods are
overal the same. The spatial and temporal lag model when cross validated
at first appears to be the strongest model with the lowest error, which
suggests that time and space lag factors are the most influential
predictors.
error_by_reg <-
reg.summary %>%
group_by(Regression) %>%
summarize(Mean_Error = mean(Error, na.rm = T),
MAE = mean(abs(Error), na.rm = T),
SD_MAE = sd(abs(Error), na.rm = T))
error_by_reg_nh <-
reg.summary %>%
group_by(Regression, name) %>%
summarize(Mean_Error = mean(Prediction - TOTAL, na.rm = T),
MAE = mean(abs(Prediction - TOTAL), na.rm = T),
SD_MAE = sd(abs(Prediction - TOTAL), na.rm = T)) %>%
ungroup()
## plot histogram of errors
error_by_reg_nh %>%
ggplot(aes(MAE)) +
geom_histogram(bins = 30, colour="black", fill = "#f20089") +
facet_wrap(~Regression, ncol = 2) +
labs(title="Distribution of MAE by Regression and Neighborhood",
x="Mean Absolute Error", y="Count")

Spatial Distribution of Errors
When we examine the temporal and spatial lag in further detail, it
becomes apparent that the neighborhoods with highest outlier in
predicted count are near the Fishtown and Southwest Philly
neighborhoods. Other than these, error is quite small throughout the
rest of Philadelphia.
reg.summary %>%
filter(Regression == "Temporal LOGO-CV: Spatial and Temporal Lag") %>%
group_by(name) %>%
st_drop_geometry() %>%
summarize(residuals = mean(Error, na.rm = T)) %>%
ungroup() %>%
left_join(phl.nh, by = c("name" = "name")) %>%
st_sf() %>%
ggplot() +
geom_sf(aes(fill = residuals)) +
scale_fill_gradient2(low = "#8900f2" , mid = "#ffb600", high = "#e500a4", name = "Error") +
labs(title = "Spatial and Temporal Lag: \nMean Absolute Error by Neighborhood", caption = "Figure 10") +
mapTheme()

Finally, when plotting predicted permits compared with observed
permits, we can see that the observed and predicted permits are fairly
close but not so much that the model is overfitting. It is therefore
generalizable and overall accurate.
reg.summary %>%
filter(Regression == "Temporal LOGO-CV: Spatial and Temporal Lag") %>%
ggplot(aes(TOTAL, Prediction)) +
geom_point() +
stat_smooth(aes(TOTAL, TOTAL),
method = "lm", se = FALSE, size = 1, colour="#ffb600") +
stat_smooth(aes(Prediction, TOTAL),
method = "lm", se = FALSE, size = 1, colour="#f20089") +
labs(title="Predicted permits as a function of observed permits",
subtitle="Orange line represents a perfect prediction; Pink line represents prediction", caption = "Figure 21") +
xlab("Actual Permits") +
ylab("Predicted Permits") +
plotTheme()

Our spatial regression model cross validates on a neighborhood level
across time, while our temporal regression cross validates year by year.
Regression error for the temporal logo cross validation with spatial and
temporal lag have the lowest mean absolute error as well as standard
deviation, which remains consistent with our other findings of that
model. It is clear that this model is generalizable, accurate, and
overall a good fit across measurements. Because cross validating on a
temporal scale appears to have the lowest mean MAE and Standard
Deviation, we can be fairly certain that time is key to predicting
permit issuance. This is also important since it means that additional
years of data will ideally be able to predict permit issuance with
future years of datasets.
st_drop_geometry(error_by_reg_nh) %>%
group_by(Regression) %>%
summarize(Mean_MAE = round(mean(MAE), 2),
SD_MAE = round(sd(MAE), 2)) %>%
kable() %>%
kable_styling("striped", full_width = F) %>%
row_spec(3, color = "black", background = "#8900f2") %>%
row_spec(4, color = "black", background = "#8900f2") %>%
row_spec(5, color = "black", background = "#ffb600") %>%
row_spec(6, color = "black", background = "#ffb600")
|
Regression
|
Mean_MAE
|
SD_MAE
|
|
Random k-fold CV: Baseline Variables
|
18.68
|
26.03
|
|
Random k-fold CV: Spatial and Temporal Lag
|
15.66
|
20.21
|
|
Spatial LOGO-CV: Baseline Variables
|
34.18
|
39.30
|
|
Spatial LOGO-CV: Spatial and Temporal Lag
|
25.90
|
45.36
|
|
Temporal LOGO-CV: Baseline Variables
|
17.26
|
22.59
|
|
Temporal LOGO-CV: Spatial and Temporal Lag
|
14.71
|
18.95
|
LS0tCnRpdGxlOiAiVGFyZ2V0aW5nIENvbW11bml0eSBMYW5kIFRydXN0IEludGVydmVudGlvbnMgdG8gRmlnaHQgRGlzcGxhY2VtZW50IgphdXRob3I6ICJMaXp6aWUgU2hhY2tuZXkgYW5kIEFsZXhhIFJpbmdlciIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIGNvZGVfZm9sZGluZzogImhpZGUiCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCmVkaXRvcl9vcHRpb25zOgogIG1hcmtkb3duOgogICAgd3JhcDogc2VudGVuY2UKICAgIAotLS0KCiMjICoqSU5UUk9EVUNUSU9OKioKCldoZXJlIGlzIGRldmVsb3BtZW50IGdvaW5nIHRvIGhhcHBlbj8gRGV2ZWxvcG1lbnQgaXMgY3JpdGljYWwgdG8gYWNjb21tb2RhdGUgZ3Jvd3RoIGFuZCBjaGFuZ2UsIHRvIHByb3ZpZGUgaG91c2luZywgaW5mcmFzdHJ1Y3R1cmUsIGFtZW5pdGllcywgZW1wbG95bWVudCwgYW5kIGNvbW11bml0eSBzcGFjZXMgZm9yIGVjb25vbWljIHByb3NwZXJpdHkgYW5kIGFmZm9yZGFibGUgc2hlbHRlci4gQnV0IGRldmVsb3BtZW50IGhhcyBpdHMgZG93bnNpZGVzLCB0b28uIFdoZW4gaW52ZXN0bWVudHMgaW4gbmVpZ2hib3Job29kcyBkcml2ZSB1cCBwcmljZXMgYW5kIHRoZSBjb3N0IG9mIGxpdmluZywgaXQgY2FuIOKAkyBlaXRoZXIgZGlyZWN0bHkgb3IgaW5kaXJlY3RseSDigJMgZm9yY2Ugb3V0IGxvbmctdGVybSByZXNpZGVudHMgd2hvIGFyZSBtb3JlIHZ1bG5lcmFibGUgdG8gY29zdCBjaGFuZ2VzLiBVbmRlcnN0YW5kaW5nIGhvdyBlY29ub21pYyBkZXZlbG9wbWVudCBjYW4gZXF1aXRhYmx5IGJlbmVmaXQgcmVzaWRlbnRzIHJhdGhlciB0aGFuIGp1c3QgcHVzaCB0aGVtIHRvIHRoZSB1cmJhbiBwZXJpcGhlcnkgY2FuIGhlbHAgZW5zdXJlIHRoYXQgdXJiYW4gcG9saWN5IGJlbmVmaXRzIGNpdHnigJlzIG1vc3QgdnVsbmVyYWJsZSByZXNpZGVudHMuIFVuZGVyc3RhbmRpbmcgcGF0dGVybnMgb2YgYW5kIHByZWRpY3RpbmcgZGV2ZWxvcG1lbnQgaXMga2V5IHRvIHRoaXMsIGFuZCBpdCdzIGNoYWxsZW5naW5nLiAgCgpBIGtleSB0byB1bmRlcnN0YW5kaW5nIHBhdHRlcm5zIG9mIGRldmVsb3BtZW50IGlzIHVuZGVyc3RhbmRpbmcgW2VuZG9nZW5vdXMgZ2VudHJpZmljYXRpb25dKCBodHRwczovL3VyYmFuc3BhdGlhbGFuYWx5c2lzLmNvbS9wb3J0Zm9saW8vcHJlZGljdGluZy1nZW50cmlmaWNhdGlvbi11c2luZy1sb25naXR1ZGluYWwtY2Vuc3VzLWRhdGEvKS4gRW5kb2dlbm91cyBnZW50cmlmaWNhdGlvbiBkZXNjcmliZXMgdGhlIGluLW1pZ3JhdGlvbiBvZiB3ZWFsdGhpZXIgcmVzaWRlbnRzIHRvIGFkamFjZW50LCBwb29yZXIgbmVpZ2hib3Job29kcywgY2F1c2luZyBwcmljZXMgdG8gcmlzZSBhbmQgbG93ZXItaW5jb21lIHJlc2lkZW50cyB0byBiZSBwcmljZWQgb3V0LiBXZSBpbmNvcnBvcmF0ZSBlbmRvZ2Vub3VzIGdlbnRyaWZpY2F0aW9uIGFzIHdlbGwgYXMgb3RoZXIgaW5kaWNhdG9ycyBvZiBnZW50cmlmaWNhdGlvbiwgbmVpZ2hib3Job29kIGFtZW5pdGllcyBhbmQgaW5mcmFzdHJ1Y3R1cmUgdG8gcHJlZGljdCB3aGljaCBhcmVhcyBvZiBQaGlsYWRlbHBoaWEgYXJlIG1vc3QgbGlrZWx5IHRvIGV4cGVyaWVuY2UgbmV3IGRldmVsb3BtZW50LiBJZiB0ZW5hbnRzIGNhbiB1bmRlcnN0YW5kIGhvdyBmYXIgaW50byB0aGUgZnV0dXJlIGRldmVsb3BtZW50IHByZXNzdXJlIGlzIGxpa2VseSB0byBwcm92aWRlIGdlbnRyaWZpY2F0aW9uIHByZXNzdXJlIGFuZCB3aGVyZSwgdGhleSBjYW4gYmV0dGVyIHRhcmdldCBhbnRpLWRpc3BsYWNlbWVudCBzdHJhdGVnaWVzLiBUaGlzIGFwcGxpY2F0aW9uIHdpbGwgaGVscCBub24tcHJvZml0IGFuZCB2b2x1bnRlZXItYmFzZWQgaG91c2luZyBqdXN0aWNlIG9yZ2FuaXphdGlvbnMgbWF4aW1pemUgdGVuYW50IG9yZ2FuaXppbmcgY2FwYWNpdHkgYW5kIHJlc291cmNlcyBmb3IgYW50aS1kaXNwbGFjZW1lbnQgYnkgZGlyZWN0aW5nIHBvbGljeSBtYWtlcnMgd2hlcmUgdG8gaW52ZXN0IGhvdXNpbmcgdHJ1c3QgZnVuZHMgJiBjb21tdW5pdHkgbGFuZCB0cnVzdHMuCgojIyMgVXNlIENhc2UKQ29tbXVuaXR5IGxhbmQgdHJ1c3RzIGFyZSBhbiBbb2Z0ZW4tY2l0ZWQgc3RyYXRlZ3kgZm9yIHByZXZlbnRpbmcgZGlzcGxhY2VtZW50XSggaHR0cHM6Ly9zbWFydGdyb3d0aGFtZXJpY2Eub3JnL3Jlc291cmNlcy9zdHJhdGVnaWVzLXRvLW1pbmltaXplLWRpc3BsYWNlbWVudC1jb21tdW5pdHktbGFuZC10cnVzdC8jOn46dGV4dD1PdmVyJTIwdGltZSUyQyUyMGNvbW11bml0eSUyMGxhbmQlMjB0cnVzdHMscmVhbCUyMGVzdGF0ZSUyMHZhbHVlcyUyMGlzJTIwcmVkdWNlZC4pLiBBIGNvbW11bml0eSBsYW5kIHRydXN0IGlzIGEgbG9uZy10ZXJtIHN0cmF0ZWd5IHRvIHByZXNlcnZlIGFmZm9yZGFibGUgaG91c2luZyBieSBwdXR0aW5nIHRoZSBsYW5kIG9mIHByb3BlcnR5IGluIGEgY29tbXVuaXR5LWNvbnRyb2xsZWQgdHJ1c3QgYW5kIG9ubHkgc2VsbGluZyB0aGUgc3RydWN0dXJlLiBCeSBzZXBhcmF0aW5nIHRoZSBsYW5kIGFuZCBwcm9wZXJ0eSBmcm9tIHRoZSBzdHJ1Y3R1cmUgKGFzIHdlbGwgYXMgaW1wb3NpbmcgZGVlZCByZXN0cmljdGlvbnMgZm9yIGFmZm9yZGFiaWxpdHkgaW5jbHVkaW5nIGEgcHJlZGV0ZXJtaW5lZCByZS1zYWxlIGZvcm11bGF0ZSkgZ3VhcmFudGVlcyBhZmZvcmRhYmlsaXR5IG92ZXIgdGhlIGxvbmcgdGVybS4gQWZmb3JkYWJpbGl0eSBtZWFzdXJlIGNhbiBoZWxwIGxvdy1pbmNvbWUgcmVzaWRlbnRzIGJ1aWxkIGVxdWl0eSB0aHJvdWdoIGhvbWVvd25lcnNoaXAgYW5kIHJldGFpbiBhZmZvcmRhYmxlIGhvdXNpbmcgZm9yIGZ1dHVyZSByZXNpZGVudHMuIEhvd2V2ZXIsIGNvbW11bml0eSBsYW5kIHRydXN0cyBbbGFjayBzdWZmaWNpZW50IGZ1bmRpbmddKCBodHRwczovL3d3dy5zaWdodGxpbmUub3JnLzIwMjEvMDgvMjMvaG93LWNhbi1jaXRpZXMtbW92ZS10aGUtbmVlZGxlLW9uLWNvbW11bml0eS1sYW5kLXRydXN0cy8pIHRvIHNjYWxlIHVwIHRvIG1lZXQgYWZmb3JkYWJsZSBob3VzaW5nIG5lZWRzIHdoZW4gZGVtYW5kIGlzIGhpZ2ggYWNyb3NzIHRoZSBib2FyZCAqKldpdGggbGltaXRlZCByZXNvdXJjZXMgYW5kIHdpZGVzcHJlYWQgbmVlZCwgdGFyZ2V0aW5nIGxhbmQgdHJ1c3QgZnVuZGluZyBpcyBjcml0aWNhbCB0byBwcmV2ZW50IGRpc3BsYWNlbWVudC4qKgoKIVtTb3VyY2U6IFZpcmdpbmlhIFN0YXRld2lkZSBDb21tdW5pdHkgTGFuZCBUcnVzdCwgaHR0cHM6Ly93d3cudnNjbHQub3JnL2ZhcS9dKENMVF9Nb2RlbC5wbmcpCgojIyMgV2Vic2l0ZSBNb2NrdXAKCmluY2x1ZGUgcGhvdG9zIG9mIG91ciBoeXBvdGhldGljYWwgd2Vic2l0ZSBhcHBsaWNhdGlvbiB0aGF0IGNvdWxkIGJlIHVzZWQgYXMgYSByZXVzbHQgb2YgdGhpcyBwcm9qZWN0LiAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKSAKb3B0aW9ucyhzY2lwZW49OTk5KQoKY29sb3JpemUgPC0gZnVuY3Rpb24oeCwgY29sb3IpIHsKICBpZiAoa25pdHI6OmlzX2xhdGV4X291dHB1dCgpKSB7CiAgICBzcHJpbnRmKCJcXHRleHRjb2xvcnslc317JXN9IiwgY29sb3IsIHgpCiAgfSBlbHNlIGlmIChrbml0cjo6aXNfaHRtbF9vdXRwdXQoKSkgewogICAgc3ByaW50ZigiPHNwYW4gc3R5bGU9J2NvbG9yOiAlczsnPiVzPC9zcGFuPiIsIGNvbG9yLAogICAgICB4KQogIH0gZWxzZSB4Cn0KCmxpYnJhcnkoTUFTUykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzZikKbGlicmFyeShzcGRlcCkKbGlicmFyeShjYXJldCkKbGlicmFyeShja2FucikKbGlicmFyeShnZ2NvcnJwbG90KSAjIHBsb3QgY29ycmVsYXRpb24gcGxvdApsaWJyYXJ5KHNmZGVwKQpsaWJyYXJ5KHRpZHljZW5zdXMpCmxpYnJhcnkoaHR0cikKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkocnBobCkKbGlicmFyeShnZW9qc29uaW8pCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShsZWhkcikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeShwc3ljaCkKbGlicmFyeShvc21kYXRhKQpsaWJyYXJ5KHRpZ3JpcykKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KEZOTikKbGlicmFyeShncmlkKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShwc2NsKQpsaWJyYXJ5KG1wYXRoKQpsaWJyYXJ5KERUKQoKCnNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3VyYmFuU3BhdGlhbC9QdWJsaWMtUG9saWN5LUFuYWx5dGljcy1MYW5kaW5nL21hc3Rlci9mdW5jdGlvbnMuciIpCgojcGFsZXR0ZQoKcGFsZXR0ZTEgPC0gYygiIzJkMDBmNyIsICIjNmEwMGY0IiwgIiM4OTAwZjIiLCAiI2JjMDBkZCIsICIjZTUwMGE0IiwgIiNmMjAwODkiLCAiI2ZmYjYwMCIpCgojTG9hZCBuZWlnaGJvcmhvb2QgZGF0YQoKcGhsLm5oIDwtIHN0X3JlYWQoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9hemF2ZWEvZ2VvLWRhdGEvbWFzdGVyL05laWdoYm9yaG9vZHNfUGhpbGFkZWxwaGlhL05laWdoYm9yaG9vZHNfUGhpbGFkZWxwaGlhLmdlb2pzb24iKSU+JQogICAgc3RfdHJhbnNmb3JtKGNycz00MzI2KSAlPiUKICBkcGx5cjo6c2VsZWN0KG5hbWUsIG1hcG5hbWUpCgpwaGwuY2QgPC0gc3RfcmVhZCgiaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzLzkyOThjMmYzZmEzMjQxZmJiMTc2ZmYxZTg0ZDMzMzYwXzAuZ2VvanNvbiIpJT4lCiAgICBzdF90cmFuc2Zvcm0oY3JzPTQzMjYpIAoKIyBMb2FkIHBlcm1pdCBkYXRhCgojICMgVVJMCgojIGNhcnRvX3VybCA9ICJodHRwczovL3BobC5jYXJ0by5jb20vYXBpL3YyL3NxbCIKIyAKIyAjIHBlcm1pdCBkYXRhCiMgdGFibGVfbmFtZSA9ICJwZXJtaXRzIgojIAojICMgcXVlcnkKIyB3aGVyZSA9ICJjb21tZXJjaWFsb3JyZXNpZGVudGlhbCA9ICdSRVNJREVOVElBTCciCiMgCiMgcXVlcnkgPSBwYXN0ZSgiU0VMRUNUICosIFNUX1kodGhlX2dlb20pIEFTIGxhdCwgU1RfWCh0aGVfZ2VvbSkgQVMgbG5nIiwKIyAgICAgICAgICAgICAgICJGUk9NIiwgdGFibGVfbmFtZSwKIyAgICAgICAgICAgICAgICJXSEVSRSIsIHdoZXJlKQojIAojIHJlc19wZXJtaXRzID0gcnBobDo6Z2V0X2NhcnRvKHF1ZXJ5LCBmb3JtYXQgPSAiY3N2IiwgYmFzZV91cmwgPSBjYXJ0b191cmwsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKcmVzX3Blcm1pdHMgPC0gcmVhZC5jc3YoIkRhdGEvcmVzX3Blcm1pdHNfMTEuMjUuY3N2IikKCmBgYAoKIyMgKipNRVRIT0RPTE9HWSoqCgpVc2luZyBQaGlsYWRlbHBoaWEgcGVybWl0IGRhdGEgYXMgYSBjYXNlIHN0dWR5LCB3ZSBjcmVhdGVkIGEgUG9pc3NvbiBtb2RlbCB0byBwcmVkaWN0IHdoZXJlIGRldmVsb3BtZW50IGlzIG1vc3QgbGlrZWx5IHRvIG9jY3VyLiBXZSBkZXZlbG9wZWQgYSBzcGF0aWFsIGxhZyBmb3IgUGhpbGFkZWxwaGlhIG5laWdoYm9yaG9vZHMgdG8gcHJlZGljdCBuZXcgY29uc3RydWN0aW9uIHBlcm1pdCBpc3N1YW5jZS4gT25lIFtzdHVkeV0oaHR0cHM6Ly9yZXNlYXJjaHJlcG9zaXRvcnkud3Z1LmVkdS9jZ2kvdmlld2NvbnRlbnQuY2dpP2FydGljbGU9OTAwNSZjb250ZXh0PWV0ZCkgZXhhbWluZWQgV2FzaGluZ3RvbiBELkMuLCB3ZWlnaGluZyBwZXJtaXQgcGF0dGVybnMgYXMgcG90ZW50aWFsIGluZGljYXRvcnMgb2YgZGV2ZWxvcG1lbnQuIFRoaXMgc3R1ZHkgaWRlbnRpZmllZCBhbmQgYXNzaWduZWQgd2VpZ2h0cyB0byB0aGUgZm9sbG93aW5nIHBlcm1pdCB0eXBlczoKLSBBZGRpdGlvbi9BZGRpdGlvbiwgQWx0ZXJhdGlvbiwgYW5kIFJlcGFpci9BbHRlcmF0aW9uIGFuZCBSZXBhaXIKLSBFbGVjdHJpYy9FbGVjdHJpYyBHZW5lcmFsL0VsZWN0cmljIEhlYXZ5IFVwCi0gUGx1bWJpbmcvUGx1bWJpbmcgYW5kIEdhcwotIFJhemUKLSBOZXcgQnVpbGRpbmcKLSBEZW1vbGl0aW9uCi0gTWVjaGFuaWNhbAotIENlcnRpZmljYXRlIG9mIE9jY3VwYW5jeQoKV2UgZXhhbWluZWQgdGhlIFBoaWxhZGVscGhpYSBwZXJtaXQgZGF0YXNldCBhbmQgZmlsdGVyZWQgZm9yIHJlc2lkZW50aWFsIGNvbnN0cnVjdGlvbiBwZXJtaXRzLiBXZSB0aGVuICBsb29rZWQgYXQgcG9wdWxhdGlvbiBsZXZlbCBkYXRhIFtpbmRpY2F0b3JzIG9mIGdlbnRyaWZpY2F0aW9uXShodHRwczovL2RyZXhlbC5lZHUvdWhjL3Jlc291cmNlcy9icmllZnMvTWVhc3VyZS1vZi1HZW50cmlmaWNhdGlvbi1mb3ItVXNlLWluLUxvbmdpdHVkaW5hbC1QdWJsaWMtSGVhbHRoLVN0dWRpZXMtaW4tdGhlLVVTLykgc3VjaCBhcyAqKnZhY2FuY3ksIHJlc2lkZW50cyB3aXRoIGJhY2hlbG9y4oCZcyBkZWdyZWVzLCBjaGFuZ2VzIGluIHJlbnRzLCBjaGFuZ2VzIGluIGhvbWUgdmFsdWVzLCBhbmQgbWVkaWFuIGluY29tZSoqIHRvIGFzc2lnbiBhbiBpbmRpY2F0b3IgdG8gZWFjaCBDZW5zdXMgdHJhY3QuIEluIGFyZWFzIHdpdGggZGVjcmVhc2luZyB2YWNhbmN5IGFuZCBpbmNyZWFzaW5nIGJhY2hlbG9y4oCZcyBkZWdyZWVzICYgaW5jb21lcywgd2UgaGF2ZSB1c2VkIHRoZXNlIGFzIGdlbnRyaWZpY2F0aW9uIGluZGljYXRvcnMgaW5kZXBlbmRlbnQgb2YgZGV2ZWxvcG1lbnQuIFdlIHRoZW4gZXhhbWluZWQgd2hpY2ggcGVybWl0IHR5cGVzIGFyZSBtb3N0IHN0cm9uZ2x5IGFzc29jaWF0ZWQgd2l0aCBnZW50cmlmaWNhdGlvbiBvciBpbnRlbnNlIGdlbnRyaWZpY2F0aW9uLCBwYXJ0aWN1bGFybHkgd2hlcmUgdGhlcmUgaXMgYSBsYXJnZSBkaWZmZXJlbmNlIGluIHRoZSBtZWFuIG51bWJlciBvZiBwZXJtaXRzIGluIGdlbnRyaWZ5aW5nIGFyZWFzIGFzIG9wcG9zZWQgdG8gbm9uLWdlbnRyaWZ5aW5nIG9yIGluZWxpZ2libGUgYXJlYXMgKHdoZXJlIGluY29tZSBhbmQgaG9tZSB2YWx1ZXMgYXJlIGFscmVhZHkgaGlnaCkuCgpXZSBlbmdpbmVlcmVkIG90aGVyIGZlYXR1cmVzIGluY2x1ZGluZyBwcm94aW1pdHkgdG8gZGVzaXJhYmxlIGFtZW5pdGllcywgc3VjaCBhcyBwdWJsaWMgdHJhbnNpdCwgY2FmZXMsIGFuZCBqb2JzLiBXZSBjcmVhdGUgc3BhdGlhbCBhbmQgdGVtcG9yYWwgbGFncyBmb3IgbWFueSBvZiBvdXIgZGVtb2dyYXBoaWMgdmFyaWFibGVzLCBsb29raW5nIGF0IHRoZSB2YWx1ZXMgb2YgbmVpZ2hib3JpbmcgY2Vuc3VzIHRyYWN0cyBhbmQgdGhlIHZhbHVlcyBmcm9tIHRoZSBwcmlvciAoMjAxNykgNS15ZWFyIEFtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkuCgpXZSB0aGVuIHJ1biBzaXggUG9pc3NvbiBsaW5lYXIgcmVncmVzc2lvbnMgdXNpbmcgdHdvIGRpZmZlcmVudCBzZXRzIG9mIHZhcmlhYmxlcyBhbmQgdGhyZWUgY3Jvc3MtdmFsaWRhdGlvbiBzdHJhdGVnaWVzLiBXZSBleGFtaW5lIHRoZSBlcnJvcnMgaW4gZGV0YWlsIHRvIGlkZW50aWZ5IHRoZSBzdHJvbmdlc3QgbW9kZWwuCgojIyAqKkRFUEVOREVOVCBWQVJJQUJMRSoqCgpJbiB0aGlzIHNlY3Rpb24sIHdlIGludmVzdGlnYXRlIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgcHJpb3IgdG8gYW55IGZpbHRlcmluZy4KCk91ciBkZXBlbmRlbnQgdmFyaWFibGUgaXMgdGhlIG51bWJlciBvZiBwZXJtaXRzIGlzc3VlZCBwZXIgY2Vuc3VzIHRyYWN0LiBUaGUgdW5pdCBvZiBhbmFseXNpcyBpcyBwZXJtaXRzIHBlciBjZW5zdXMgdHJhY3QgYmVjYXVzZSBvZiBjZW5zdXMgZGF0YSBhdmFpbGFiaWxpdHkgYW5kIHRoZSBmYWN0IHRoYXQgZmVkZXJhbCBhbmQgc3RhdGUgZnVuZGluZyBvZnRlbiBmbG93cyB0byBhcmVhcyB2aWEgY2Vuc3VzIHRyYWN0IG9yIHppcCBjb2RlIGVsaWdpYmlsaXR5LiBXZSB0aW1lIGFuZCBvciBzcGFjZSB0byBzZWUgaWYgdGhlcmUgdGhlcmUgYXJlIGFueSBwYXR0ZXJucyB0byBtaXNzaW5nIHBlcm1pdCBkYXRhIHNvIGFzIHRvIGJldHRlciBkZXRlcm1pbmUgd2hpY2ggeWVhcnMgdG8gdGVzdCBhbmQgdHJhaW4gb24uIAoKYGBge3J9CiMgU2VlIGlmIHRoZXJlIGFyZSBhbnkgcGF0dGVybnMgdG8gbWlzc2luZyBzcGF0aWFsIGRhdGEKCnJlc19wZXJtaXRzICU+JQogIGdyb3VwX2J5KHllYXIgPSB5ZWFyKGFzLkRhdGUocGVybWl0aXNzdWVkYXRlKSkpICU+JQogIHN1bW1hcmlzZV9hbGwoZnVucyhzdW0oaXMubmEoLikpKSkgJT4lCmRhdGF0YWJsZShvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMTIpLAogICAgICAgICAgICBzdHlsZSA9ICJhdXRvIikKCmBgYAoKV2hlbiBsb29raW5nIGF0IGNvdW5jaWwgZGlzdHJpY3QgdnMuIHllYXIgZGlzdHJpYnV0aW9uIG9mIHBlcm1pdCBkYXRhLCB0aGVyZSBhcmUgcGxlbnR5IG9mIHBlcm1pdHMgaW4gZWFjaCBkaXN0cmljdCwgYnV0IHdpZGUgZGlmZmVyZW5jZXMgYnkgeWVhci4gUGVybWl0dGluZyBhY3Rpdml0eSBpbiBlYXJsaWVyIHllYXJzIGlzIG11Y2ggbG93ZXIgdGhhbiAyMDIwIHRvIDIwMjMuIEFsdGhvdWdoIDIwMjAgaXMgdHlwaWNhbGx5IGNvbnNpZGVyZWQgYW4gYW5vbWFseSwgcGVybWl0IGlzc3VhbmNlIGFuZCBkZXZlbG9wbWVudCB3YXMgc3Ryb25nIGR1ZSB0byBsb3cgaW50ZXJlc3QgcmF0ZXMuIFRoZXJlZm9yZSwgd2UgdHJhaW5lZCAyMDIwIGFuZCAyMDIxIHBlcm1pdCBpc3N1YW5jZSBkYXRhIHRvIHRlc3Qgb24gMjAyMiBkZXZlbG9wbWVudCBhY3Rpdml0eS4gQXMgYSByZXN1bHQsIHdlIGdvdCByaWQgb2YgbWlzc2luZyBzcGF0aWFsIGRhdGEKCmBgYHtyfQpyZXNfcGVybWl0cyAlPiUKICBncm91cF9ieShjb3VuY2lsX2Rpc3RyaWN0KSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIGdncGxvdChhZXMoeSA9IGFzLmZhY3Rvcihjb3VuY2lsX2Rpc3RyaWN0KSwgeCA9IGNvdW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIiNmZmI2MDAiKSArIAogIHBsb3RUaGVtZSgpICsgCiAgbGFicyh0aXRsZSA9ICJQZXJtaXRzIGJ5IENvdW5jaWwgRGlzdHJpY3QiLCBjYXB0aW9uID0gIkZpZ3VyZSAxIikKCnJlc19wZXJtaXRzICU+JQogIGdyb3VwX2J5KHllYXIgPSB5ZWFyKGFzLkRhdGUocGVybWl0aXNzdWVkYXRlKSkpICU+JQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogIGdncGxvdChhZXMgKHggPSB5ZWFyLCB5ID0gY291bnQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiI2ZmYjYwMCIpICsKICBwbG90VGhlbWUoKSArIAogIGxhYnModGl0bGUgPSAiUGVybWl0cyBieSBZZWFyIiwgY2FwdGlvbiA9ICJGaWd1cmUgMiIpCgpyZXNfcGVybWl0cyA8LSByZXNfcGVybWl0cyAlPiUKICBmaWx0ZXIoIWlzLm5hKGxuZykgJiAhaXMubmEobGF0KSkgJT4lCiAgc3RfYXNfc2YoY29vcmRzID0gYygibG5nIiwibGF0IiksY3JzPTQzMjYpCgpgYGAKCk5leHQsIHdlIGV4YW1pbmVkIHBlcm1pdCB0eXBlcyBhbmQgY291bnRzIHRvIHVuZGVyc3RhbmQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwZXJtaXQgdHlwZXMgc3BlY2lmaWMgdG8gUGhpbGFkZWxwaGlhLiBXaGVuIGxvb2tpbmcgYXQgdGhlIGNvdW50IG9mIHBlcm1pdCB0eXBlcywgYXMgcmVwcmVzZW50ZWQgYmVsb3cgYnkgbiwgd2UgY2FuIHNlZSB0aGF0IHRoZSBwZXJtaXQgdHlwZSBhbG9uZSBkb2VzIG5vdCBpbmRpY2F0ZSB3aGF0IHR5cGUgb2Ygd29yayBpcyBiZWluZyBkb25lLiBOZXcgY29uc3RydWN0aW9uIHdpbGwgbmVjZXNzaXRhdGUgZGlmZmVyZW50IHBlcm1pdCB0eXBlcywgc3VjaCBhcyBwbHVtYmluZywgbWVjaGFuaWNhbCwgb3IgZWxlY3RyaWNhbCBwZXJtaXRzLCBidXQgdGhlc2UgY2FuIGFsc28gYmUgY291bnRlZCBpbiByZWd1bGFyIG1haW50ZW5hbmNlLiBUaGVyZWZvcmUsIGZpbHRlcmluZyBvbmx5IGJhc2VkIG9uIG5ldyBjb25zdHJ1Y3Rpb24gbWF5IGJlIG1pc2xlYWRpbmcuIFdlIGxvb2tlZCBhdCBjZW5zdXMgZGF0YSBuZXh0IHRvIGJlZ2luIHVuZGVyc3RhbmRpbmcgaG93IHBlcm1pdCBkYXRhIGlzIGFzc29jaWF0ZWQgd2l0aCBkZW1vZ3JhcGhpY3MgYW5kIG91ciBnZW50cmlmaWNhdGlvbiBpbmRpY2F0b3IuIAoKYGBge3Igd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIH0KcmVzX3Blcm1pdHMgJT4lCiAgZ3JvdXBfYnkocGVybWl0ZGVzY3JpcHRpb24sIHR5cGVvZndvcmspICU+JQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lCiAgYXJyYW5nZShwZXJtaXRkZXNjcmlwdGlvbiwgZGVzYyhuKSkgJT4lCiAgZGF0YXRhYmxlKG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSAxMCksCiAgICAgICAgICAgIHN0eWxlID0gImF1dG8iKQoKCgpgYGAKCmBgYHtyIHJlc3VsdHMgPSAnaGlkZSd9CnBobF90cmFjdHM0MzI2IDwtIHRyYWN0cyhzdGF0ZSA9ICJQQSIsCiAgICAgICAgICAgICAgICAgICAgIGNvdW50eSA9ICJQaGlsYWRlbHBoaWEiLAogICAgICAgICAgICAgICAgICAgICB5ZWFyID0gIjIwMjAiKSAlPiUKICBzZWxlY3QoR0VPSUQsIGdlb21ldHJ5KSAlPiUKICBzdF9zZXRfY3JzKDQzMjYpCgpyZXNfcGVybWl0czIgPC0gcmVzX3Blcm1pdHMgJT4lCiAgZ3JvdXBfYnkocGVybWl0ZGVzY3JpcHRpb24sIHR5cGVvZndvcmspICU+JQogIG11dGF0ZShuID0gbigpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KHBlcm1pdHR5cGUsIHBlcm1pdGRlc2NyaXB0aW9uLCB0eXBlb2Z3b3JrLCBnZW9tZXRyeSwgcGVybWl0aXNzdWVkYXRlKSAlPiUKICAjam9pbiB0byBjZW5zdXMgdHJhY3QKICBzdF9qb2luKHBobF90cmFjdHM0MzI2KQpgYGAKCldoZW4gd2UgbG9vayBhdCBudW1iZXIgb2YgcGVybWl0cyBieSB0eXBlIHBlciB0cmFjdCBhcyBzZWVuIGJlbG93LCBpdCBiZWNvbWVzIGFwcGFyZW50IHRoZXJlIGFyZSBhIHZlcnkgaGlnaCBjb25jZW50cmF0aW9uIG9mIGFsbCBwZXJtaXRzIGluIHRoZSBGaXNodG93biwgUG9ydCBSaWNobW9uZCBhcmVhLiBXaGVuIHdlIGxvb2sgYnkgeWVhciwgd2Ugc2VlIHRoYXQgc29tZSBvZiB0aGlzIGlzIGJlY2F1c2Ugb2YgZGF0YSB1bmF2YWlsYWJpbGl0eSBpbiBlYXJsaWVyIHllYXJzIGZvbGxvd2VkIGJ5IGEgZmx1eCBvZiBwZXJtaXQgaXNzdWFuY2UgZnJvbSAyMDIwIHRvIDIwMjIuIAoKU28gd2hhdCBoYXBwZW5lZCB0aGVzZSB5ZWFycz8gCgotIEludGVyZXN0IHJhdGVzIHdlcmUgYXQgYSBoaXN0b3JpY2FsIGxvdyBpbiAyMDIwIHRvIDIwMjIsIGluIHBhcnQgdG8gZW50aWNlIGVjb25vbWljIGFjdGl2aXR5IHRvIGJlIGFzIGVudGljaW5nIGFzIHBvc3NpYmxlIGFmdGVyIHRoZSBDT1ZJRC0xOSBwYW5kZW1pYyBicm91Z2h0IHRoZSBlY29ub215IHRvIGEgc2NyZWVjaGluZyBoYWx0LiAKIVtBdmVyYWdlIGludGVyZXN0IFJhdGVzIDIwMTcgLSAyMDIzLCBTb3VyY2U6IGh0dHBzOi8vdGFrZS1wcm9maXQub3JnL2VuL3N0YXRpc3RpY3MvaW50ZXJlc3QtcmF0ZS91bml0ZWQtc3RhdGVzL10oaW50ZXJlc3QtcmF0ZXMucG5nKQoKLSBNb3JlIGxvY2FsbHksIFBoaWxhZGVscGhpYSBwYXNzZWQgYSB0YXggcG9saWN5IGluIDIwMDAgdG8gcHJvdmlkZSBhIHRheCBhYmF0ZW1lbnQgb24gbmV3IGhvdXNpbmcgY29uc3RydWN0aW9uIHVudGlsIFtpdCBleHBpcmVkIGluIDIwMjJdKGh0dHBzOi8vd3d3LmlucXVpcmVyLmNvbS9uZXdzL3BoaWxhZGVscGhpYS9pbnEyL3BoaWxhZGVscGhpYS1wcm9wZXJ0eS10YXgtYWJhdGVtZW50LW5laWdoYm9yaG9vZC1tYXBzLWRhdGEtMjAyMzAyMTQuaHRtbCkuIFRoZSBwb2xpY3kgZ29hbCB3YXMgdG8gc3B1ciBkZXZlbG9wbWVudCBmb3IgcmVzaWRlbnRpYWwgcHJvcGVydHkgb3duZXJzIGJ5IHByb3ZpZGluZyBhIDEwLXllYXIgdGF4IGFiYXRlbWVudCBvbiB0aGUgdmFsdWUgb2YgaW1wcm92ZW1lbnRzLiBUaGlzIHBhcnRpYWxseSBleHBsYWlucyB0aGUgdGltZSBsYWcgb2Ygc29tZSBkZXZlbG9wbWVudCwgYnV0IGl0IHJlbWFpbnMgdW5jbGVhciB3aHkgdGhlIGRldmVsb3BtZW50IHdhcyBzbyBjb25jZW50cmF0ZWQgc3BhdGlhbGx5LiAKCk91ciBuZXh0IGRhdGEgYW5hbHlzaXMgc3RlcCBpcyB0byBkcm9wIHRoZSB5ZWFyIGFuZCBsb29rIG9ubHkgYXQgcGVybWl0cyBpbiBhbmQgYWZ0ZXIgMjAxOC4gSXQgaXMgbGlrZWx5IHRoYXQgbW9yZSBwZXJtaXRzIGFyZSBpc3N1ZWQgaW4gYXJlYXMgd2hlcmUgdGhlc2UgY2hhbmdlcyBhcmUgYWxyZWFkeSBoYXBwZW5pbmcuIAoKYGBge3J9CgojIFBJQ0sgMQoKdHJhY3RfcGVybWl0c195ZWFyIDwtIHJlc19wZXJtaXRzMiAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgbXV0YXRlKHBlcm1pdCA9IHBhc3RlKHBlcm1pdGRlc2NyaXB0aW9uLCAiLSIsIHR5cGVvZndvcmspLAogICAgICAgICB5ZWFyID0geWVhcihhcy5EYXRlKHBlcm1pdGlzc3VlZGF0ZSkpKSAlPiUKICBzZWxlY3QoLXBlcm1pdGlzc3VlZGF0ZSwgLXBlcm1pdGRlc2NyaXB0aW9uLCAtcGVybWl0dHlwZSwgLXR5cGVvZndvcmspICU+JQogIGdhdGhlcihWYXJpYWJsZSwgdmFsdWUsIC1HRU9JRCwgLXllYXIpICU+JQogIGRwbHlyOjpjb3VudChWYXJpYWJsZSwgdmFsdWUsIEdFT0lELCB5ZWFyKSAlPiUKICBncm91cF9ieShHRU9JRCwgeWVhcikgJT4lCiAgbXV0YXRlKFRPVEFMID0gc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3ByZWFkKGtleSA9IHZhbHVlLCB2YWx1ZSA9IG4pICU+JQogIHNlbGVjdCgtVmFyaWFibGUpICU+JQogIHJlcGxhY2UoaXMubmEoLiksIDApCgojdG90YWwgcGVybWl0cwp0cmFjdF9wZXJtaXRzX3llYXIgJT4lCiAgbGVmdF9qb2luKHBobF90cmFjdHM0MzI2KSAlPiUKICBzdF9zZigpICU+JQogIGZpbHRlcih5ZWFyID4gMjAxNCAmIHllYXIgPCAyMDIzKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX3NmKGFlcyhmaWxsID0gY3V0KFRPVEFMLCBicmVha3MgPSA4KSkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlMSkgKwogIG1hcFRoZW1lKCkgKwogIGxhYnModGl0bGUgPSAiUGVybWl0cyBiZXR3ZWVuIDIwMTQgLSAyMDIzIiwgY2FwdGlvbiA9ICJGaWd1cmUgMyIpCgoKI2J5IHllYXIgLSB0aGVyZSBhcmUgYSBsb3Qgb2YgdHJhY3RzIG1pc3NpbmcgaW4gZWFybGllciB5ZWFycwojIHRyYWN0X3Blcm1pdHNfeWVhciAlPiUKIyAgIGxlZnRfam9pbihwaGxfdHJhY3RzNDMyNikgJT4lCiMgICBzdF9zZigpICU+JQojICAgZmlsdGVyKHllYXIgPiAyMDE0ICYgeWVhciA8PSAyMDIzKSAlPiUKIyAgIGdncGxvdCgpKwojICAgZ2VvbV9zZihhZXMoZmlsbCA9IFRPVEFMKSkgKwojICAgc2NhbGVfZmlsbF9iaW5uZWQobi5icmVha3M9OCkgKwojICAgZmFjZXRfd3JhcCh+eWVhcikgKwojICAgbWFwVGhlbWUoKQoKYGBgCgoKCmBgYHtyfQp0cmFjdF9wZXJtaXRzIDwtIHJlc19wZXJtaXRzMiAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgbXV0YXRlKHBlcm1pdCA9IHBhc3RlKHBlcm1pdGRlc2NyaXB0aW9uLCAiLSIsIHR5cGVvZndvcmspLAogICAgICAgICB5ZWFyID0geWVhcihhcy5EYXRlKHBlcm1pdGlzc3VlZGF0ZSkpKSAlPiUKICBmaWx0ZXIoeWVhciA+PSAyMDE4KSAlPiUKICBzZWxlY3QoLXBlcm1pdGlzc3VlZGF0ZSwgLXBlcm1pdGRlc2NyaXB0aW9uLCAtcGVybWl0dHlwZSwgLXR5cGVvZndvcmssIC15ZWFyKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIHZhbHVlLCAtR0VPSUQpICU+JQogIGRwbHlyOjpjb3VudChWYXJpYWJsZSwgdmFsdWUsIEdFT0lEKSAlPiUKICBncm91cF9ieShHRU9JRCkgJT4lCiAgbXV0YXRlKFRPVEFMID0gc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc3ByZWFkKGtleSA9IHZhbHVlLCB2YWx1ZSA9IG4pICU+JQogIHNlbGVjdCgtVmFyaWFibGUpICU+JQogIHJlcGxhY2UoaXMubmEoLiksIDApCgpgYGAKCiMjICoqUEVSTUlUIFNFTEVDVElPTioqCgojIyMgQ2Vuc3VzIERhdGEKCkJ1dCBhIGtleSBjb25zaWRlcmF0aW9uIGlzOiBbZG9lcyBkZW1hbmQgbGVhZCBzdXBwbHldKGh0dHBzOi8vam91cm5hbHMuc2FnZXB1Yi5jb20vZG9pL2Z1bGwvMTAuMTE3Ny8wMDQyMDk4MDIwOTQwNTk2KSBvciB2aWNlIHZlcnNhPyBXaGljaCBjb21lcyBmaXJzdCwgZGV2ZWxvcG1lbnQgb3IgZ2VudHJpZmljYXRpb24/CgpUaGVyZSBhcmUgdHdvIHRoZW9yaWVzIG9mIG5laWdoYm9yaG9vZCBjaGFuZ2UgYXMgaXQgcmVsYXRlcyB0byB0aGlzLiAKLSAxKSBIb3VzaW5nIGRlbWFuZCBvZiBtaWRkbGUtY2xhc3MsIG1vc3RseSB3aGl0ZSByZXNpZGVudHMgZW5jb3VyYWdlcyB0aGVtIHRvIGxlYXZlIG5laWdoYm9yaG9vZHMgdG8gc2VlayBvdXQgbW9yZSBhZmZvcmRhYmxlIG5laWdoYm9yaG9vZHMuIAotIDIpIERldmVsb3BtZW50IHN1cHBseSBvZiBuZXcgY29uc3RydWN0aW9uIGluIGhpc3RvcmljYWxseSBwb29yZXIgbmVpZ2hib3Job29kcyBlbmNvdXJhZ2VzIGFuIGluZmx1eCBvZiBuZXcgcmVzaWRlbnRzLCB3aG8gYXJlIGVuY291cmFnZWQgdG8gbW92ZSB0byBhIG5laWdoYm9yaG9vZCBieSB0aGUgc3VwcGx5IG9mIGhvdXNpbmcuIAoKVGhlIGFib3ZlIHN0dWR5IGZvdW5kIHRoYXQgaG91c2luZyBtYXJrZXQgZ3Jvd3RoIChpLmUuIG5ldyBwZXJtaXQgaXNzdWFuY2UpIGRpZCBub3QgcHJlZGljdCBmdXR1cmUgaW5jcmVhc2UgaW4gZ2VudHJpZmljYXRpb247IHJhdGhlciwgZGV2ZWxvcG1lbnQgZm9sbG93ZWQgbmVpZ2hib3Job29kIGNoYW5nZS4gR2VudHJpZmljYXRpb24gZmVhdHVyZXMgdGhlbXNlbHZlcyBjb3VsZCBiZSBjb25zaWRlcmVkIGluZGljYXRvcnMgb2YgZnV0dXJlIGRldmVsb3BtZW50LiAKCkFzIGEgcmVzdWx0IG9mIHBlcm1pdCBhbmFseXNpcywgd2Ugc2VsZWN0ZWQgcGVybWl0cyB0aGF0IHdlcmUgYXNzb2NpYXRlZCB3aXRoIGdlbnRyaWZpY2F0aW9uIGluZGljYXRvcnMuIApVc2luZyBbcHJpb3IgcmVzZWFyY2ggb24gZ2VudHJpZmljYXRpb24gaW5kaWNhdG9yc10oaHR0cHM6Ly9yZXNlYXJjaHJlcG9zaXRvcnkud3Z1LmVkdS9jZ2kvdmlld2NvbnRlbnQuY2dpP2FydGljbGU9OTAwNSZjb250ZXh0PWV0ZCkgYW5kIHVzaW5nIHB1YmxpY2x5IGF2YWlsYWJsZSBjZW5zdXMgZGF0YSwgd2UgZXhhbWluZWQgdGhlIGZvbGxvd2luZyBkZW1vZ3JhcGhpYyBjaGFyYWN0ZXJpc3RpY3M6IAoKLSBBbGwgQ2Vuc3VzIHRyYWN0cyBpbiBQaGlsYWRlbHBoaWEsIDVZIEFDUyBmb3IgMjAyMSBhbmQgZm9yIDIwMTYKLSBNZWRpYW4gSEggaW5jb21lLCBDUEktaW5mbGF0aW9uIGFkanVzdGVkCi0gTWVkaWFuIGhvbWUgdmFsdWUsIENQSS1pbmZsYXRpb24gYWRqdXN0ZWQKLSBNZWRpYW4gcmVudCwgQ1BJLWluZmxhdGlvbiBhZGp1c3RlZAotIE1lZGlhbiB5ZWFyIHN0cnVjdHVyZSBidWlsdAotIFRlbnVyZSAtIG93bmVyLW9jY3VwaWVkCi0gTnVtYmVyIG9mIGhvdXNpbmcgdW5pdHMKLSBOdW1iZXIgb2YgdmFjYW5jaWVzCi0gQ291bnQgb2Ygc2Vjb25kIG1vcnRnYWdlcwotIFBlcmNlbnQgd2hpdGUgcG9wdWxhdGlvbgotIFBlcmNlbnQgd2l0aCBCYWNoZWxvcidzIGRlZ3JlZQoKV2UgY2FsY3VsYXRlZDogcmVudCB0byBpbmNvbWUgcmF0aW8sIG5ldCBjaGFuZ2VzIG9mIG11bHRpcGxlIGluZGljYXRvcnMgZnJvbSAyMDE3IHRvIDIwMjIsIGFuZCBwZXJjZW50IGNoYW5nZXMgZnJvbSAyMDE3IHRvIDIwMjIuIE1lZGlhbiByZW50cywgdmFsdWVzLCBhbmQgaW5jb21lcyB3ZXJlIGFkanVzdGVkIHRvIDIwMjIgZG9sbGFycy4KCmBgYHtyIHJlc3VsdHMgPSAnaGlkZSd9CgpDUEkyMDE3IDwtIDI0Mi44MzkKQ1BJMjAyMiA8LSAyODEuMTQ4CgpDUEkgPC0gQ1BJMjAyMi9DUEkyMDE3CgojIE5PVEU6IGN1cnJlbnRseSByZXBsYWNpbmcgTkEgdmFsdWVzIHdpdGggYSB2ZXJ5IHNtYWxsIHZhbHVlLi4uCgpwaGxDZW5zdXMyMiA8LSAKICBnZXRfYWNzKGdlb2dyYXBoeSA9ICJ0cmFjdCIsIAogICAgICAgICAgdmFyaWFibGVzID0gYygiQjAxMDAzXzAwMSIsICJCMTkwMTNfMDAxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICJCMDIwMDFfMDAyIiwgIkIyNTA3N18wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjI1MDY0XzAwMSIsICJCMjUwMzVfMDAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkIyNTAwM18wMDEiLCAiQjI1MDAzXzAwMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJCMjUwMDFfMDAxIiwgIkIyNTAwMl8wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjI1MDAyXzAwMyIsICJCMjUwODFfMDAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkIyNTA4MV8wMDQiLCAiQjE1MDAzXzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJCMTUwMDNfMDIyIgogICAgICAgICAgICAgICAgICAgICAgICApLCAKICAgICAgICAgIHllYXIgPSAyMDIyLCAKICAgICAgICAgIGRhdGFzZXQgPSAiYWNzNSIsCiAgICAgICAgICBzdGF0ZSA9ICJQQSIsIAogICAgICAgICAgZ2VvbWV0cnkgPSBGQUxTRSwgCiAgICAgICAgICBjb3VudHkgPSBjKCJQaGlsYWRlbHBoaWEiKSwKICAgICAgICAgIG91dHB1dCA9ICJ3aWRlIikgJT4lCiAgcmVuYW1lKFRvdGFsX1BvcCA9ICBCMDEwMDNfMDAxRSwKICAgICAgICAgTWVkX0luYyA9IEIxOTAxM18wMDFFLAogICAgICAgICBXaGl0ZV9Qb3AgPSBCMDIwMDFfMDAyRSwKICAgICAgICAgTWVkX1ZhbHVlID0gQjI1MDc3XzAwMUUsCiAgICAgICAgIE1lZF9SZW50ID0gQjI1MDY0XzAwMUUsCiAgICAgICAgIE1lZF9TdHJ1Y3R1cmUgPSBCMjUwMzVfMDAxRSwKICAgICAgICAgT3duZXJfdW5pdiA9IEIyNTAwM18wMDFFLAogICAgICAgICBPd25lcnMgPSBCMjUwMDNfMDAyRSwKICAgICAgICAgVG90X1VuaXRzID0gQjI1MDAxXzAwMUUsCiAgICAgICAgIFZhY191bml2ID0gQjI1MDAyXzAwMUUsCiAgICAgICAgIFZhY2FudHMgPSBCMjUwMDJfMDAzRSwKICAgICAgICAgTW9ydF9zdGF0dXNfdW5pdiA9IEIyNTA4MV8wMDFFLAogICAgICAgICBTZWNfTW9ydCA9IEIyNTA4MV8wMDRFLAogICAgICAgICBCYWNoX3VuaXYgPSBCMTUwMDNfMDAxRSwKICAgICAgICAgQmFjaCA9IEIxNTAwM18wMjJFCiAgICAgICAgICkgJT4lCiAgZHBseXI6OnNlbGVjdChUb3RhbF9Qb3AsIE1lZF9JbmMsIFdoaXRlX1BvcCwKICAgICAgICAgICAgICAgIE1lZF9WYWx1ZSwgTWVkX1JlbnQsIE1lZF9TdHJ1Y3R1cmUsIAogICAgICAgICAgICAgICAgT3duZXJfdW5pdiwgT3duZXJzLCBUb3RfVW5pdHMsCiAgICAgICAgICAgICAgICBWYWNfdW5pdiwgVmFjYW50cywgTW9ydF9zdGF0dXNfdW5pdiwgCiAgICAgICAgICAgICAgICBTZWNfTW9ydCwgQmFjaF91bml2LCBCYWNoLCAgR0VPSUQpICU+JQogIG11dGF0ZShQZXJjZW50X1doaXRlID0gV2hpdGVfUG9wIC8gVG90YWxfUG9wLAogICAgICAgICBQZXJjZW50X093bmVyID0gT3duZXJzIC8gT3duZXJfdW5pdiwKICAgICAgICAgUGVyY2VudF9WYWNhbnQgPSBWYWNhbnRzIC8gVmFjX3VuaXYsCiAgICAgICAgIFBlcmNlbnRfMm1vcnQgPSBTZWNfTW9ydCAvIE1vcnRfc3RhdHVzX3VuaXYsCiAgICAgICAgIFBlcmNlbnRfYmFjaCA9IEJhY2ggLyBCYWNoX3VuaXYsCiAgICAgICAgIFJlbnRfSW5jb21lX1JhdGlvID0gTWVkX1JlbnQgLyAoTWVkX0luYy8xMikKICAgICAgICAgKSAlPiUKICAjcmVwbGFjZShpcy5uYSguKSwgLjAwMDAwMDAxKSAlPiUKICBzZWxlY3QoLWVuZHNfd2l0aCgiX3VuaXYiKSkKCnBobENlbnN1czE3IDwtIAogIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgCiAgICAgICAgICB2YXJpYWJsZXMgPSBjKCJCMDEwMDNfMDAxIiwgIkIxOTAxM18wMDEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIkIwMjAwMV8wMDIiLCAiQjI1MDc3XzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJCMjUwNjRfMDAxIiwgIkIyNTAzNV8wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjI1MDAzXzAwMSIsICJCMjUwMDNfMDAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkIyNTAwMV8wMDEiLCAiQjI1MDAyXzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJCMjUwMDJfMDAzIiwgIkIyNTA4MV8wMDEiLAogICAgICAgICAgICAgICAgICAgICAgICAiQjI1MDgxXzAwNCIsICJCMTUwMDNfMDAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkIxNTAwM18wMjIiCiAgICAgICAgICAgICAgICAgICAgICAgICksIAogICAgICAgICAgeWVhciA9IDIwMTcsIAogICAgICAgICAgc3RhdGUgPSAiUEEiLCAKICAgICAgICAgIGdlb21ldHJ5ID0gRkFMU0UsIAogICAgICAgICAgY291bnR5PWMoIlBoaWxhZGVscGhpYSIpLAogICAgICAgICAgb3V0cHV0ID0gIndpZGUiKSAlPiUKICByZW5hbWUoVG90YWxfUG9wID0gIEIwMTAwM18wMDFFLAogICAgICAgICBNZWRfSW5jID0gQjE5MDEzXzAwMUUsCiAgICAgICAgIFdoaXRlX1BvcCA9IEIwMjAwMV8wMDJFLAogICAgICAgICBNZWRfVmFsdWUgPSBCMjUwNzdfMDAxRSwKICAgICAgICAgTWVkX1JlbnQgPSBCMjUwNjRfMDAxRSwKICAgICAgICAgTWVkX1N0cnVjdHVyZSA9IEIyNTAzNV8wMDFFLAogICAgICAgICBPd25lcl91bml2ID0gQjI1MDAzXzAwMUUsCiAgICAgICAgIE93bmVycyA9IEIyNTAwM18wMDJFLAogICAgICAgICBUb3RfVW5pdHMgPSBCMjUwMDFfMDAxRSwKICAgICAgICAgVmFjX3VuaXYgPSBCMjUwMDJfMDAxRSwKICAgICAgICAgVmFjYW50cyA9IEIyNTAwMl8wMDNFLAogICAgICAgICBNb3J0X3N0YXR1c191bml2ID0gQjI1MDgxXzAwMUUsCiAgICAgICAgIFNlY19Nb3J0ID0gQjI1MDgxXzAwNEUsCiAgICAgICAgIEJhY2hfdW5pdiA9IEIxNTAwM18wMDFFLAogICAgICAgICBCYWNoID0gQjE1MDAzXzAyMkUKICAgICAgICAgKSAlPiUKICBkcGx5cjo6c2VsZWN0KFRvdGFsX1BvcCwgTWVkX0luYywgV2hpdGVfUG9wLAogICAgICAgICAgICAgICAgTWVkX1ZhbHVlLCBNZWRfUmVudCwgTWVkX1N0cnVjdHVyZSwgCiAgICAgICAgICAgICAgICBPd25lcl91bml2LCBPd25lcnMsIFRvdF9Vbml0cywKICAgICAgICAgICAgICAgIFZhY191bml2LCBWYWNhbnRzLCBNb3J0X3N0YXR1c191bml2LCAKICAgICAgICAgICAgICAgIFNlY19Nb3J0LCBCYWNoX3VuaXYsIEJhY2gsIEdFT0lEKSAlPiUKICBtdXRhdGUoCiAgICBNZWRfUmVudCA9IE1lZF9SZW50ICogQ1BJLAogICAgTWVkX1ZhbHVlID0gTWVkX1ZhbHVlICogQ1BJLAogICAgUGVyY2VudF9XaGl0ZSA9IFdoaXRlX1BvcCAvIFRvdGFsX1BvcCwKICAgICAgICAgUGVyY2VudF9Pd25lciA9IE93bmVycyAvIE93bmVyX3VuaXYsCiAgICAgICAgIFBlcmNlbnRfVmFjYW50ID0gVmFjYW50cyAvIFZhY191bml2LAogICAgICAgICBQZXJjZW50XzJtb3J0ID0gU2VjX01vcnQgLyBNb3J0X3N0YXR1c191bml2LAogICAgICAgICAgICAgICAgICBQZXJjZW50X2JhY2ggPSBCYWNoIC8gQmFjaF91bml2LAogICAgICAgICBSZW50X0luY29tZV9SYXRpbyA9ICBNZWRfUmVudCAvIChNZWRfSW5jLzEyKSwKICAgICAgICAgKSAlPiUKICAjcmVwbGFjZShpcy5uYSguKSwgLjAwMDAwMDAxKSAlPiUKICAgIHNlbGVjdCgtZW5kc193aXRoKCJfdW5pdiIpKQoKIyBJbnRlcnBvbGF0ZSBiYXNlZCBvbiBwb3B1bGF0aW9uIGluIG9yZGVyIHRvIHRyYW5zbGF0ZSAyMDE3IHRyYWN0cyB0byAyMDIwIHRyYWN0cwojIHdvcmthcm91bmQgYmVjYXVzZSB0aWR5Y2Vuc3VzIHdhc24ndCBkb3dubG9hZGluZyBnZW9tZXRyeQoKI2dldCBqdXN0IHBvcHVsYXRpb24gZnJvbSBwaGxDZW5zdXMKcG9wXzIyIDwtIHBobENlbnN1czIyICU+JQogIHNlbGVjdChHRU9JRCwgVG90YWxfUG9wKQoKI2dldCB0cmFjdHM7IGpvaW4gcG9wdWxhdGlvbiB0byB0cmFjdHMKcGhsX3RyYWN0czIyIDwtIHRyYWN0cyhzdGF0ZSA9ICJQQSIsCiAgICAgICAgICAgICAgICAgICAgIGNvdW50eSA9ICJQaGlsYWRlbHBoaWEiLAogICAgICAgICAgICAgICAgICAgICB5ZWFyID0gIjIwMjIiKSAlPiUKICBsZWZ0X2pvaW4ocG9wXzIyKSAlPiUKICBzdF9zZigpCgojam9pbiB0cmFjdHMgdG8gQ2Vuc3VzIGRhdGEKcGhsQ2Vuc3VzMjIgPC0gcGhsQ2Vuc3VzMjIgJT4lCiAgbGVmdF9qb2luKHBobF90cmFjdHMyMiAlPiUKICAgICAgICAgICAgICBzZWxlY3QoR0VPSUQsIGdlb21ldHJ5KSkgJT4lCiAgc3Rfc2YoKQoKI2dldCAyMDE3IHRyYWN0cwpwaGxfdHJhY3RzMTcgPC0gdHJhY3RzKHN0YXRlID0gIlBBIiwKICAgICAgICAgICAgICAgICAgICAgY291bnR5ID0gIlBoaWxhZGVscGhpYSIsCiAgICAgICAgICAgICAgICAgICAgIHllYXIgPSAiMjAxNyIpCgojam9pbiB0cmFjdHMgdG8gMjAxNyBDZW5zdXMgZGF0YQpwaGxDZW5zdXMxNyA8LSBwaGxDZW5zdXMxNyAlPiUKICBsZWZ0X2pvaW4ocGhsX3RyYWN0czE3ICU+JQogICAgICAgICAgICAgIHNlbGVjdChHRU9JRCxnZW9tZXRyeSkpICU+JQogIHN0X3NmKCkKCnNmX3VzZV9zMihGQUxTRSkKCmNlbnN1czE3X2ludGVycG9sYXRlX3B3IDwtIGludGVycG9sYXRlX3B3KAogIHBobENlbnN1czE3LAogIHBobENlbnN1czIyLAogIHRvX2lkID0gIkdFT0lEIiwKICBleHRlbnNpdmUgPSBUUlVFLCAKICB3ZWlnaHRzID0gcGhsX3RyYWN0czIyLAogIHdlaWdodF9jb2x1bW4gPSAiVG90YWxfUG9wIiwKICBjcnMgPSA0MjY5KQoKY2Vuc3VzXzIyX2FuZF9jb21wYXJpc29uIDwtIHBobENlbnN1czIyICU+JQogIGxlZnRfam9pbihzdF9kcm9wX2dlb21ldHJ5KGNlbnN1czE3X2ludGVycG9sYXRlX3B3KSwgCiAgICAgICAgICAgIGJ5ID0gIkdFT0lEIiwKICAgICAgICAgICAgc3VmZml4ID0gYygiXzIwMjIiLCAiXzIwMTciKSkgJT4lCiAgbXV0YXRlKFRvdGFsX1BvcF9ORVQgPSBUb3RhbF9Qb3BfMjAyMiAtIFRvdGFsX1BvcF8yMDE3LCAKICAgICAgICAgTWVkX0luY19ORVQgPSBNZWRfSW5jXzIwMjIgLSBNZWRfSW5jXzIwMTcsIAogICAgICAgICBXaGl0ZV9Qb3BfTkVUID0gV2hpdGVfUG9wXzIwMjIgLSBXaGl0ZV9Qb3BfMjAxNywKICAgICAgICAgTWVkX1ZhbHVlX05FVCA9IE1lZF9WYWx1ZV8yMDIyIC0gTWVkX1ZhbHVlXzIwMTcsIAogICAgICAgICBNZWRfUmVudF9ORVQgPSBNZWRfUmVudF8yMDIyIC0gTWVkX1JlbnRfMjAxNywgCiAgICAgICAgIE93bmVyc19ORVQgPSBPd25lcnNfMjAyMiAtIE93bmVyc18yMDE3LCAKICAgICAgICAgVG90X1VuaXRzX05FVCA9IFRvdF9Vbml0c18yMDIyIC0gVG90X1VuaXRzXzIwMTcsCiAgICAgICAgIFZhY2FudHNfTkVUID0gVmFjYW50c18yMDIyIC0gVmFjYW50c18yMDE3LCAKICAgICAgICAgU2VjX01vcnRfTkVUID0gU2VjX01vcnRfMjAyMiAtIFNlY19Nb3J0XzIwMTcsCiAgICAgICAgIFBlcmNlbnRfV2hpdGVfTkVUID0gUGVyY2VudF9XaGl0ZV8yMDIyIC0gUGVyY2VudF9XaGl0ZV8yMDE3LAogICAgICAgICBQZXJjZW50X093bmVyX05FVCA9IFBlcmNlbnRfT3duZXJfMjAyMiAtIFBlcmNlbnRfT3duZXJfMjAxNywKICAgICAgICAgUGVyY2VudF9WYWNhbnRfTkVUID0gUGVyY2VudF9WYWNhbnRfMjAyMiAtIFBlcmNlbnRfVmFjYW50XzIwMTcsCiAgICAgICAgIFBlcmNlbnRfMm1vcnRfTkVUID0gUGVyY2VudF8ybW9ydF8yMDIyIC0gUGVyY2VudF8ybW9ydF8yMDE3LAogICAgICAgICBSZW50X0luY29tZV9SYXRpb19ORVQgPSBSZW50X0luY29tZV9SYXRpb18yMDIyIC0gUmVudF9JbmNvbWVfUmF0aW9fMjAxNywKICAgICAgICAgQmFjaF9ORVQgPSBCYWNoXzIwMjIgLSBCYWNoXzIwMTcsCiAgICAgICAgIFBlcmNlbnRfYmFjaF9ORVQgPSBQZXJjZW50X2JhY2hfMjAyMiAtIFBlcmNlbnRfYmFjaF8yMDE3LAogICAgICAgICAKICAgICAgICAgVG90YWxfUG9wX1BDVCA9IGlmZWxzZShUb3RhbF9Qb3BfMjAxNz4wLCAoVG90YWxfUG9wX05FVCAvIFRvdGFsX1BvcF8yMDE3KSwgTkEpKjEwMCwgCiAgICAgICAgIE1lZF9JbmNfUENUID0gaWZlbHNlKE1lZF9JbmNfMjAxNyA+MCwgKE1lZF9JbmNfTkVUIC8gTWVkX0luY18yMDE3KSwgTkEpKjEwMCwgCiAgICAgICAgIFdoaXRlX1BvcF9QQ1QgPSBpZmVsc2UoV2hpdGVfUG9wXzIwMTcgPjAsIChXaGl0ZV9Qb3BfTkVUIC8gV2hpdGVfUG9wXzIwMTcpLCBOQSkqMTAwLAogICAgICAgICBNZWRfVmFsdWVfUENUID0gaWZlbHNlKE1lZF9WYWx1ZV8yMDE3PjAsIChNZWRfVmFsdWVfTkVUIC8gTWVkX1ZhbHVlXzIwMTcpLCBOQSkqMTAwLCAKICAgICAgICAgTWVkX1JlbnRfUENUID0gaWZlbHNlKE1lZF9SZW50XzIwMTc+MCwgKE1lZF9SZW50X05FVCAvIE1lZF9SZW50XzIwMTcpLE5BKSoxMDAsIAogICAgICAgICBPd25lcnNfUENUID0gaWZlbHNlKE93bmVyc18yMDE3PjAsICggT3duZXJzX05FVCAvIE93bmVyc18yMDE3KSxOQSkqMTAwLCAKICAgICAgICAgVG90X1VuaXRzX1BDVCA9IGlmZWxzZShUb3RfVW5pdHNfMjAxNz4wLCAoVG90X1VuaXRzX05FVCAvIFRvdF9Vbml0c18yMDE3KSxOQSkqMTAwLAogICAgICAgICBWYWNhbnRzX1BDVCA9IGlmZWxzZShWYWNhbnRzXzIwMTc+MCwoVmFjYW50c19ORVQgLyBWYWNhbnRzXzIwMTcpLE5BKSoxMDAsIAogICAgICAgICBTZWNfTW9ydF9QQ1QgPSBpZmVsc2UoU2VjX01vcnRfMjAxNz4wLChTZWNfTW9ydF9ORVQgLyBTZWNfTW9ydF8yMDE3KSxOQSkqMTAwLAogICAgICAgICBQZXJjZW50X1doaXRlX1BDVCA9aWZlbHNlKFBlcmNlbnRfV2hpdGVfMjAxNz4wLChQZXJjZW50X1doaXRlX05FVCAvIFBlcmNlbnRfV2hpdGVfMjAxNyksTkEpKjEwMCwKICAgICAgICAgCiAgICAgICAgICMgaXMgaXQgYmFkIHRvIGNhbGN1bGF0ZSBwZXJjZW50IGNoYW5nZSB1c2luZyBwZXJjZW50YWdlcz8KICAgICAgICAgCiAgICAgICAgIFBlcmNlbnRfT3duZXJfUENUID0gaWZlbHNlKFBlcmNlbnRfT3duZXJfMjAxNz4wLChQZXJjZW50X093bmVyX05FVCAvIFBlcmNlbnRfT3duZXJfMjAxNyksTkEpKjEwMCwKICAgICAgICAgUGVyY2VudF9WYWNhbnRfUENUID0gaWZlbHNlKFBlcmNlbnRfVmFjYW50XzIwMTc+MCwoUGVyY2VudF9WYWNhbnRfTkVUIC8gUGVyY2VudF9WYWNhbnRfMjAxNyksTkEpKjEwMCwKICAgICAgICAgUGVyY2VudF8ybW9ydF9QQ1QgPSBpZmVsc2UoUGVyY2VudF8ybW9ydF8yMDE3PjAsKFBlcmNlbnRfMm1vcnRfTkVUIC8gUGVyY2VudF8ybW9ydF8yMDE3KSxOQSkqMTAwLAogICAgICAgICAKICAgICAgICAgUmVudF9JbmNvbWVfUmF0aW9fUENUID0gaWZlbHNlKFJlbnRfSW5jb21lX1JhdGlvXzIwMTc+MCwgKFJlbnRfSW5jb21lX1JhdGlvX05FVCAvIFJlbnRfSW5jb21lX1JhdGlvXzIwMTcpLE5BKSoxMDAsCiAgICAgICAgIAogICAgICAgICBQZXJjZW50X2JhY2hfUENUID0gaWZlbHNlKFBlcmNlbnRfYmFjaF8yMDE3PjAsIChQZXJjZW50X2JhY2hfTkVUIC8gUGVyY2VudF9iYWNoXzIwMTcpLE5BKSoxMDApICU+JQogIAogIHJlbG9jYXRlKEdFT0lEKQoKYGBgCgojIyMgR2VudHJpZmljYXRpb24gSW5kaWNhdG9yCgpXZSBkZXZlbG9wZWQgYSBnZW50cmlmaWNhdGlvbiBpbmRpY2F0b3IgbWV0aG9kb2xvZ3kgYmFzZWQgb24gW2EgRHJleGVsIHN0dWR5XShodHRwczovL2RyZXhlbC5lZHUvdWhjL3Jlc291cmNlcy9icmllZnMvTWVhc3VyZS1vZi1HZW50cmlmaWNhdGlvbi1mb3ItVXNlLWluLUxvbmdpdHVkaW5hbC1QdWJsaWMtSGVhbHRoLVN0dWRpZXMtaW4tdGhlLVVTLykuIFRoaXMgaXMganVzdCBvbmUgcG9zc2libGUgaW5kaWNhdG9yIGFuZCBjb3VsZCBiZSBpbXByb3ZlZCB1cG9uIG9yIGNoYW5nZWQgYnkgaW5jb3Jwb3JhdGluZyBhZGRpdGlvbmFsIGRlbW9ncmFwaGljIHZhcmlhYmxlcy4gCgpUaGVzZSBmb3VyIGdlbnRyaWZpY2F0aW9uIGNhdGVnb3JpZXMgd2UgdXNlIHRvIGNoYXJhY3Rlcml6ZSB0cmFjdHMgYXJlOgoKKiBJbmVsaWdpYmxlCiogTm90IGdlbnRyaWZ5aW5nCiogR2VudHJpZnlpbmcKKiBJbnRlbnNlbHkgR2VudHJpZnlpbmcKCldlIGNhbiB0aGVuIHNlZSBmb3Igd2hpY2ggY2hhcmFjdGVyaXphdGlvbnMgdGhlcmUgYXJlIGRpZmZlcmVuY2VzIGluIHBlcm1pdCB0eXBlcy4gSWYgdGhlIG1lYW4gbnVtYmVyIG9mIHBlcm1pdHMgZm9yIGNlbnN1cyB0cmFjdHMgdGhhdCBhcmUgZ2VudHJpZnlpbmcgb3IgaW50ZW5zZWx5IGdlbnRyaWZ5aW5nIGlzIGxhcmdlciB0aGFuIGZvciB0cmFjdHMgdGhhdCBhcmUgaW5lbGlnaWJsZSBvciBub3QgZ2VudHJpZnlpbmcsIHdlIGtlZXAgdGhhdCBwZXJtaXQgdHlwZS4gCgoKYGBge3IgcmVzdWx0cyA9ICdoaWRlJ30KCmluZGljYXRvciA8LSBjZW5zdXNfMjJfYW5kX2NvbXBhcmlzb24gJT4lCiAgbXV0YXRlKGluZGljYXRvciA9IAogICAgICAgICAgIGNhc2Vfd2hlbigKICAgICAgICAgICAgIAogICAgICAgICAgICAgIyB0cmFjdHMgd2l0aCA8IDUwIHBlb3BsZSBvciBpbiB0b3AgcXVhcnRpbGUgb2YgbWVkaWFuIGluY29tZSBhcmUgZXhjbHVkZWQKICAgICAgICAgICAgIAogICAgICAgICAgICAgVG90YWxfUG9wXzIwMjIgPD0gNTAgfCBNZWRfSW5jXzIwMTcgPiBxdWFudGlsZShNZWRfSW5jXzIwMTcsIC43NSwgbmEucm0gPSBUUlVFKSB+ICJJbmVsaWdpYmxlIiwKICAgICAgICAgCiAgICAgICAgICAgICAjIHRyYWN0cyB3aXRoIGJlbG93IG1lZGlhbiBpbmNyZWFzZSBpbiBwY3Qgd2l0aCBiYWNoZWxvcidzIGRlZ3JlZSwKICAgICAgICAgICAgICMgYmVsb3cgbWVkaWFuIGNoYW5nZXMgaW4gcmVudCBhbmQgaG9tZSB2YWx1ZXMgYXJlIG5vdCBnZW50cmlmeWluZwogICAgICAgICAgICAgUGVyY2VudF9iYWNoX1BDVCA8IG1lZGlhbihQZXJjZW50X2JhY2hfTkVULCBuYS5ybSA9IFRSVUUpIHwgCiAgICAgICAgICAgICAgIChNZWRfVmFsdWVfTkVUIDwgbWVkaWFuKE1lZF9WYWx1ZV9ORVQsIG5hLnJtID0gVFJVRSkgJgogICAgICAgICAgICAgICAgICBNZWRfUmVudF9ORVQgPCBtZWRpYW4oTWVkX1JlbnRfTkVULCBuYS5ybSA9IFRSVUUpKSB+ICJOb3QgZ2VudHJpZnlpbmciLAogICAgICAgICAKICAgICAgICAgICAgIyB0cmFjdHMgd2l0aCBhYm92ZS1tZWRpYW4gcGVyY2VudCBpbmNyZWFzZXMgaW4gYmFjaGVsb3IgZGVncmVlIGF0dGFpbm1lbnQKICAgICAgICAgICAgIyBhbmQgRUlUSEVSIGIvdCAuNSBhbmQgLjc1IHBlcmNlbnRpbGUgaW5jcmVhc2UgaW4gaG9tZSB2YWx1ZXMgb3IgcmVudHMgPSBnZW50cmlmeWluZwogICAgICAgICAKICAgICAgICAgUGVyY2VudF9iYWNoX1BDVCA+PSBtZWRpYW4oUGVyY2VudF9iYWNoX05FVCwgbmEucm0gPSBUUlVFKSAmICgKICAgICAgICAgICAoTWVkX1ZhbHVlX05FVCA+PSBxdWFudGlsZShNZWRfVmFsdWVfTkVULCAuNTAsIG5hLnJtID0gVFJVRSkgJgogICAgICAgICAgICAgIE1lZF9WYWx1ZV9ORVQgPCBxdWFudGlsZShNZWRfVmFsdWVfTkVULCAuNzUsIG5hLnJtID0gVFJVRSkpIHwKICAgICAgICAgICAgIChNZWRfUmVudF9ORVQgPj0gcXVhbnRpbGUoTWVkX1JlbnRfTkVULCAuNTAsIG5hLnJtID0gVFJVRSkgJiAKICAgICAgICAgICAgICAgIE1lZF9SZW50X05FVCA8IHF1YW50aWxlKE1lZF9SZW50X05FVCwgLjc1LCBuYS5ybSA9IFRSVUUpKQogICAgICAgICAgIAogICAgICAgICApIH4gIkdlbnRyaWZ5aW5nIiwKICAgICAgICAgCiAgICAgICAgICMgdHJhY3RzIHdpdGggYWJvdmUtbWVkaWFuIHBlcmNlbnQgaW5jcmVhc2VzIGluIGJhY2hlbG9yIGRlZ3JlZSBhdHRhaW5tZW50CiAgICAgICAgICAgICMgYW5kIEVJVEhFUiBpbiAuNzUgcGVyY2VudGlsZSBpbmNyZWFzZSBpbiBob21lIHZhbHVlcyBvciByZW50cyA9IGludGVuc2VseSBnZW50cmlmeWluZwogICAgICAgICAKICAgICAgICAgUGVyY2VudF9iYWNoX1BDVCA+PSBtZWRpYW4oUGVyY2VudF9iYWNoX05FVCwgbmEucm0gPSBUUlVFKSAmIAogICAgICAgICAgIChNZWRfVmFsdWVfTkVUID49IHF1YW50aWxlKE1lZF9WYWx1ZV9ORVQsLjc1LCBuYS5ybSA9IFRSVUUpIHwgCiAgICAgICAgICAgICAgTWVkX1JlbnRfTkVUID49IHF1YW50aWxlKE1lZF9SZW50X05FVCwuNzUsIG5hLnJtID0gVFJVRSkKICAgICAgICAgICApCiAgICAgICAgICAKICAgICAgICAgIH4gIkludGVuc2VseSBnZW50cmlmeWluZyIpCiAgKSAlPiUKICAKICBzZWxlY3QoR0VPSUQsIGluZGljYXRvciwgVG90YWxfUG9wXzIwMjIsIFRvdGFsX1BvcF8yMDE3LCAKICAgICAgICAgTWVkX0luY18yMDIyLCBNZWRfSW5jXzIwMTcsCiAgICAgICAgIFBlcmNlbnRfYmFjaF9ORVQsIE1lZF9WYWx1ZV9ORVQsIE1lZF9SZW50X05FVCkgCgpgYGAKCiMjIyBQZXJtaXQgU2VsZWN0aW9uCgpXZSBjYW4gc2VlIGJlbG93IGhvdyBlYWNoIHBlcm1pdCB0eXBlIHJlbGF0ZXMgdG8gZWFjaCBnZW50cmlmaWNhdGlvbiBpbmRpY2F0b3IsIGFuZCB3ZSBjYW4gYWxzbyBjYWxjdWxhdGUgdGhlIG5ldCBkaWZmZXJlbmNlIGFuZCBwZXJjZW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbnVtYmVyIG9mIHBlcm1pdHMgaW4gbm90IGdlbnRyaWZ5aW5nIHRyYWN0cyB2ZXJzdXMgaW50ZW5zZWx5IGdlbnRyaWZ5aW5nIHRyYWN0cy4gCgpgYGB7cn0KaW5kaWNhdG9yX3RhYmxlIDwtIGluZGljYXRvciAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KC1Ub3RhbF9Qb3BfMjAyMiwgLVRvdGFsX1BvcF8yMDE3LCAKICAgICAgICAgLU1lZF9JbmNfMjAyMiwgLU1lZF9JbmNfMjAxNywKICAgICAgICAgLVBlcmNlbnRfYmFjaF9ORVQsIC1NZWRfVmFsdWVfTkVULCAtTWVkX1JlbnRfTkVUKSAlPiUKICAgIGxlZnRfam9pbih0cmFjdF9wZXJtaXRzKSAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgc2VsZWN0KC1HRU9JRCkgJT4lCiAgZ2F0aGVyKFZhcmlhYmxlLCB2YWx1ZSwgLWluZGljYXRvcikgJT4lCiAgZ3JvdXBfYnkoaW5kaWNhdG9yLCBWYXJpYWJsZSkgJT4lCiAgc3VtbWFyaXNlX2lmKGlzLm51bWVyaWMsIG1lYW4sIG5hLnJtID0gVFJVRSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHNwcmVhZChrZXkgPSBpbmRpY2F0b3IsIHZhbHVlID0gdmFsdWUpICU+JQogIG11dGF0ZV9pZihpcy5udW1lcmljLCByb3VuZCwgZGlnaXRzID0gMykgJT4lCiAgc2VsZWN0KFZhcmlhYmxlLCBJbmVsaWdpYmxlLCBgTm90IGdlbnRyaWZ5aW5nYCwgR2VudHJpZnlpbmcsIGBJbnRlbnNlbHkgZ2VudHJpZnlpbmdgKSAKCmtlZXAgPC0gaW5kaWNhdG9yX3RhYmxlICU+JQogIG11dGF0ZShnX3RvX25vbl9nX1BDVCA9IHJvdW5kKChHZW50cmlmeWluZyAtIGBOb3QgZ2VudHJpZnlpbmdgKSAvIGBOb3QgZ2VudHJpZnlpbmdgLCA0KSAqMTAwLAogICAgICAgICBpZ190b19ub25fZ19QQ1QgPSByb3VuZCgoYEludGVuc2VseSBnZW50cmlmeWluZ2AgLSBgTm90IGdlbnRyaWZ5aW5nYCkgLyBgTm90IGdlbnRyaWZ5aW5nYCwgNCkgKjEwMCkgJT4lCiAgZmlsdGVyKEdlbnRyaWZ5aW5nID49IDEgJiBgSW50ZW5zZWx5IGdlbnRyaWZ5aW5nYD49MSAmIGdfdG9fbm9uX2dfUENUID4gMCAmIGlnX3RvX25vbl9nX1BDVCA+IDApICU+JQogIHNlbGVjdCgtZ190b19ub25fZ19QQ1QsIC1pZ190b19ub25fZ19QQ1QpCgprZWVwICU+JQogIGRhdGF0YWJsZSgpCgprZWVwIDwtIHVuaXF1ZShrZWVwJFZhcmlhYmxlKSAKCmBgYAoKSGVyZSwgd2UgZmlsdGVyIG9ubHkgZm9yIHBlcm1pdHMgd2hlcmUgdGhlcmUgaXMgYSBub24temVybyBwZXJjZW50IGRpZmZlcmVuY2UgYmV0d2VlbiBpbnRlbnNlbHkgZ2VudHJpZnlpbmcgdHJhY3RzIGFuZCBub24tZ2VudHJpZnlpbmcgdHJhY3RzLCBhbmQga2VlcCBvbmx5IHRob3NlIHBlcm1pdHMgYW5kIGF0dGFjaCB0aGF0IHRvIGNlbnN1cyBkYXRhLCBzbyBlYWNoIHRyYWN0IGlzIGFzc2lnbmVkIGFuIGluZGljYXRvciBmb3Igd2hldGhlciBvciBub3QgaXQgaXMgZ2VudHJpZnlpbmcuIAoKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9Cgp0cmFjdF9wZXJtaXRzX2ZpbmFsIDwtIHJlc19wZXJtaXRzMiAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgbXV0YXRlKHBlcm1pdCA9IHBhc3RlKHBlcm1pdGRlc2NyaXB0aW9uLCAiLSIsIHR5cGVvZndvcmspLAogICAgICAgICB5ZWFyID0geWVhcihhcy5EYXRlKHBlcm1pdGlzc3VlZGF0ZSkpKSAlPiUKICBmaWx0ZXIoeWVhciA+PSAyMDE4ICYgcGVybWl0ICVpbiUga2VlcCkgJT4lCiAgc2VsZWN0KC1wZXJtaXRpc3N1ZWRhdGUsIC1wZXJtaXRkZXNjcmlwdGlvbiwgLXBlcm1pdHR5cGUsIC10eXBlb2Z3b3JrKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIHZhbHVlLCAtR0VPSUQsIC15ZWFyKSAlPiUKICBkcGx5cjo6Y291bnQoVmFyaWFibGUsIHZhbHVlLCBHRU9JRCwgeWVhcikgJT4lCiAgZ3JvdXBfYnkoR0VPSUQsIHllYXIpICU+JQogIG11dGF0ZShUT1RBTCA9IHN1bShuKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHNwcmVhZChrZXkgPSB2YWx1ZSwgdmFsdWUgPSBuKSAlPiUKICBzZWxlY3QoR0VPSUQsIHllYXIsIFRPVEFMKSAlPiUKICByZXBsYWNlKGlzLm5hKC4pLCAwKSAKCmNlbnN1c19wZXJtaXRzIDwtIAogIGV4cGFuZC5ncmlkKEdFT0lEID0gdW5pcXVlKHRyYWN0X3Blcm1pdHNfZmluYWwkR0VPSUQpLAogICAgICAgICAgICAgIHllYXI9dW5pcXVlKHRyYWN0X3Blcm1pdHNfZmluYWwkeWVhcikpICU+JQogIGxlZnRfam9pbihpbmRpY2F0b3IgJT4lIHNlbGVjdChHRU9JRCwgaW5kaWNhdG9yKSkgJT4lCiAgbGVmdF9qb2luKHRyYWN0X3Blcm1pdHNfZmluYWwpICU+JQogIGxlZnRfam9pbihjZW5zdXNfMjJfYW5kX2NvbXBhcmlzb24gJT4lIHN0X2Ryb3BfZ2VvbWV0cnkoKSkgJT4lCiAgbXV0YXRlKFRPVEFMID0gaWZlbHNlKGlzLm5hKFRPVEFMKSwgMCwgVE9UQUwpKSAlPiUKICBmaWx0ZXIoVG90YWxfUG9wXzIwMjIgPiAwKSAlPiUgCiAgc2VsZWN0KC1nZW9tZXRyeSkgJT4lCiAgbGVmdF9qb2luKHBobF90cmFjdHM0MzI2KSAlPiUKICBzdF9hc19zZigpCiAgCmNlbnN1c19wZXJtaXRzJGluZGljYXRvciA8LSBmYWN0b3IoY2Vuc3VzX3Blcm1pdHMkaW5kaWNhdG9yLCBvcmRlcmVkID0gVFJVRSwgbGV2ZWxzID0gYygiSW5lbGlnaWJsZSIsICJOb3QgZ2VudHJpZnlpbmciLCAiR2VudHJpZnlpbmciLCAiSW50ZW5zZWx5IGdlbnRyaWZ5aW5nIikpCgpgYGAKCiMjIyBFeHBsb3JhdG9yeSBBbmFseXNpcyBvZiBGaWx0ZXJlZCBEZXBlbmRlbnQgVmFyaWFibGUgCgpOb3cgdGhhdCB3ZSBoYXZlIGZpbHRlcmVkIHdoYXQgd2lsbCB1bHRpbWF0ZWx5IGJlY29tZSBvdXIgZGVwZW5kZW50IHZhcmlhYmxlLCB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlICptYW55KiBjZW5zdXMgdHJhY3RzIHdoZXJlIHRoZXJlIGFyZSBubyBwZXJtaXRzIGlzc3VlZCBhY3Jvc3MgYWxsIHllYXJzLiAKCmBgYHtyfQoKZ2dwbG90KGNlbnN1c19wZXJtaXRzKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBUT1RBTCksIGJpbnMgPSA0MCwgZmlsbCA9ICIjZmZiNjAwIikgKwogIGxhYnModGl0bGUgPSAiUGVybWl0IERpc3RyaWJ1dGlvbiIsIGNhcHRpb24gPSAiRmlndXJlIDQiKQoKYGBgCgpUaGVyZSB3YXMgYSBodWdlIHNwaWtlIG9mIHBlcm1pdHMgaXNzdWVkIGluIDIwMjAgY2l0eXdpZGUsIGxpa2VseSBzcHVycmVkIGJ5IHRoZSBhbm5vdW5jZWQgW3JvbGxiYWNrIG9mIHRoZSB0YXggYWJhdGVtZW50IHBlcmlvZF0oaHR0cHM6Ly93d3cyLmxhdy50ZW1wbGUuZWR1LzEwcS9waGlsYWRlbHBoaWEtY2l0eS1jb3VuY2lsLWVuYWN0cy1zd2VlcGluZy1jaGFuZ2VzLXRvLXRoZS10YXgtYWJhdGVtZW50LXByb2dyYW0vIzp+OnRleHQ9SW4lMjBQaGlsYWRlbHBoaWElMkMlMjByZXNpZGVudGlhbCUyMHByb3BlcnRpZXMlMjBhcmUsZmlsZWQlMjBhZnRlciUyMERlY2VtYmVyJTIwMjElMkMlMjAyMDIwLikgYW5kIHRoZSBoaXN0b3JpY2FsbHkgbG93IGludGVyZXN0IHJhdGVzLiAKQXMgYSByZXN1bHQsIFsyMDIxIHdhcyBhIG1ham9yIHBlcm1pdCBib29tIHllYXJdKGh0dHBzOi8vd3d3LmlucXVpcmVyLmNvbS9yZWFsLWVzdGF0ZS9ob3VzaW5nL2hvdXNpbmctZGV2ZWxvcG1lbnQtcGVybWl0cy1hYmF0ZW1lbnQtcGhpbGFkZWxwaGlhLWRlY2xpbmUtMjAyMjEyMDcuaHRtbCkgYW5kIFtEZWNlbWJlciAyMDIxIGV4cGVyaWVuY2VkIHRoZSB0aGUgaGlnaGVzdCBjb3VudCBvZiBuZXcgY29uc3RydWN0aW9uIHBlcm1pdHMgaXNzdWVkIHNpbmNlIEphbnVhcnkgMjAxNl0oaHR0cHM6Ly93d3cuZWNvbm9teWxlYWd1ZS5vcmcvcmVzb3VyY2VzL2luc2lkZS1waGlsYWRlbHBoaWFzLWltbWluZW50LWNvbnN0cnVjdGlvbi1ib29tKS4gVGhlIFtib29tIGNvbnRpbnVlZCBpbiAyMDIyXShodHRwczovL3doeXkub3JnL2FydGljbGVzL3BoaWxseS1pcy1vbi10cmFjay10by1idWlsZC1hLXJlY29yZC1udW1iZXItb2YtYXBhcnRtZW50cy1pbi0yMDIyLWFuYWx5c3Qtc2F5cy8pIGFuZCBuZXdzIGFydGljbGVzIHRocm91Z2hvdXQgdGhlIHllYXJzIGNvbW1lbnRlZCBvbiB0aGUgWyJhYnN1cmQiIG51bWJlciBvZiBuZXcgY29uc3J1Y3Rpb24gcGVybWl0cyBpc3N1ZWRdKGh0dHBzOi8vd3d3LmJpc25vdy5jb20vcGhpbGFkZWxwaGlhL25ld3MvY29uc3RydWN0aW9uLWRldmVsb3BtZW50L3BoaWxhZGVscGhpYS1jb25zdHJ1Y3Rpb24tYm9vbS0yMDIxLXRheC1hYmF0ZW1lbnQtMTExODc0KSBpbiBQaGlsYWRlbHBoaWEuIAoKCmBgYHtyfQpjZW5zdXNfcGVybWl0cyAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpc2UoVE9UQUwgPSBzdW0oVE9UQUwpKSAlPiUKICBmaWx0ZXIoIWlzLm5hKHllYXIpKSAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgZ2dwbG90KGFlcyAoeCA9IHllYXIsIHkgPSBUT1RBTCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICIjZmZiNjAwIikgKwogIHBsb3RUaGVtZSgpICsKICBsYWJzKHRpdGxlID0gIlBlcm1pdHMgYnkgWWVhciIsIGNhcHRpb24gPSAiRmlndXJlIDUiKQpgYGAKCk5hdGlvbmFsIGFuZCBjaXR5LXdpZGUgcG9saWNpZXMgbWF5IGJlIGluZmx1ZW5jaW5nIGRldmVsb3BtZW50IG9uIGEgY2l0eSBzY2FsZS4gR2VudHJpZmljYXRpb24gaW5kaWNhdG9ycyBwcm92aWRlIGEgY2x1ZSBhcyB0byB3aGljaCB0cmFjdHMgYXJlIGV4cGVyaWVuY2luZyBjaGFuZ2UsIG9yIHZlcnkgcmFwaWQgY2hhbmdlLCBpZiBhdCBhbGwuIEJ1dCB3ZSBoYXZlbid0IHlldCBob25lZCBpbiBvbiBuZWlnaGJvcmhvb2RzIHRvIGd1aWRlIG91ciBmZWF0dXJlIGVuZ2luZWVyaW5nLiBXZSBsb29rZWQgYXQgcGVybWl0cyBieSBuZWlnaGJvcmhvb2QgZm9yIHNvbWUgY2x1ZXMgYW5kIGNvdWxkIGltbWVkaWF0ZWx5IHNlZSB0aGF0IHRoZXJlIHdlcmUgaGlnaCBwZXJtaXRzIGlzc3VlZCBmb3IgbmVpZ2hib3Job29kcyB0aGF0IGhhdmUgYmVlbiB0aGUgc3ViamVjdCBvZiBsb2NsYSBhcnRpY2xlcyBhYm91dCBnZW50cmlmaWNhdGlvbiwgaW5jbHVkaW5nIFtGaXNodG93bl0oaHR0cHM6Ly93d3cuZm9yYmVzLmNvbS9zaXRlcy9wZXRlcnRheWxvci8yMDE4LzA1LzAyL2hvdy1maXNodG93bi1waGlsYWRlbHBoaWEtYmVjYW1lLWFtZXJpY2FzLWhvdHRlc3QtbmV3LW5laWdoYm9yaG9vZC8/c2g9MTQ5NjVkODczMmU1KSwgW1BvaW50IEJyZWV6ZV0oaHR0cHM6Ly93d3cuaW5xdWlyZXIuY29tL25ld3MvcG9pbnQtYnJlZXplLXRpb2dhLW5pY2V0b3duLWdlbnRyaWZpY2F0aW9uLXBoaWxhZGVscGhpYS1jZW5zdXMtbmVpZ2hib3Job29kLWNoYW5nZS0yMDIwMDEwOS5odG1sKSwgYW5kIFtMb3dlciBLZW5zaW5ndG9uXShodHRwczovL3d3dy5pbnF1aXJlci5jb20vcmVhbC1lc3RhdGUvaW5nYS1zYWZmcm9uL2tlbnNpbmd0b24tZ2VudHJpZmljYXRpb24tc2hpZnQtY2FwaXRhbC1kcnVncy1vcGlvaWRzLWltcGFjdC1zZXJ2aWNlcy1uZWlnaGJvcmhvb2QtZGV2ZWxvcG1lbnQtaG91c2luZy1tYWtlbi1mZWxzLWZ1bmQtaW5nYS1zYWZmcm9uLTIwMjEwMjEwLmh0bWwpCgpgYGB7cn0KcmVzX3Blcm1pdHMzIDwtIHJlc19wZXJtaXRzICU+JQogIGdyb3VwX2J5KHBlcm1pdGRlc2NyaXB0aW9uLCB0eXBlb2Z3b3JrKSAlPiUKICBtdXRhdGUobiA9IG4oKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIHNlbGVjdChwZXJtaXR0eXBlLCBwZXJtaXRkZXNjcmlwdGlvbiwgdHlwZW9md29yaywgZ2VvbWV0cnksIHBlcm1pdGlzc3VlZGF0ZSkgJT4lCiAgc3Rfam9pbihwaGwubmgpICU+JQogICAgbXV0YXRlKHBlcm1pdCA9IHBhc3RlKHBlcm1pdGRlc2NyaXB0aW9uLCAiLSIsIHR5cGVvZndvcmspLAogICAgICAgICB5ZWFyID0geWVhcihhcy5EYXRlKHBlcm1pdGlzc3VlZGF0ZSkpKSAlPiUKICBmaWx0ZXIoeWVhcj49MjAxOCAmIHBlcm1pdCAlaW4lIGtlZXApCgpyZXNfcGVybWl0czMgPC0gcmVzX3Blcm1pdHMzICU+JQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUKICBncm91cF9ieShtYXBuYW1lLCB5ZWFyKSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKG4pKQoKcmVzX3Blcm1pdHMzICU+JQogIGRhdGF0YWJsZSgpCmBgYAoKV2hlbiB3ZSBsb29rIGF0IHRoaXMgb3ZlciB0aW1lLCB3ZSBjYW4gc2VlIGNsZWFybHkgdGhhdCB0aGUgbmVpZ2hib3Job29kIHBhdHRlcm5zIHJlbWFpbiB0aGUgc2FtZSwgd2l0aCB0aGUgc2FtZSBuZWlnaGJvcmhvb2RzIHJlY2VpdmluZyBwZXJtaXRzIGFjcm9zcyBhIDYteWVhciBwZXJpb2QuIAoKYGBge3J9CgpyZXNfcGVybWl0czMgPC0gcmVzX3Blcm1pdHMzICU+JQogIGxlZnRfam9pbihwaGwubmgpCgpyZXNfcGVybWl0czMgJT4lCiAgdW5ncm91cCgpICU+JQogIHN0X2FzX3NmKCkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fc2YoYWVzKGZpbGwgPSBjdXQobiwgYnJlYWtzID0gNykpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZTEpICsKICBmYWNldF93cmFwKH55ZWFyKSArCiAgbWFwVGhlbWUoKSArCiAgbGFicyh0aXRsZSA9ICJQZXJtaXRzIGJ5IFllYXIsIFBoaWxhZGVscGhpYSIsIGNhcHRpb24gPSAiRmlndXJlIDYiKQoKCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBleHBsb3JlZCBnZW50cmlmaWNhdGlvbiBpbmRpY2F0b3JzIGFjcm9zcyBQaGlsYWRlbHBoaWEgbmVpZ2hib3Job29kcyBhbmQgdHJhY3RzLCBwZXJtaXRzIGlzc3VlZCBvdmVyIHRpbWUgYW5kIHNwYWNlLCBhbmQgc29tZSBiYXNpYyBkZW1vZ3JhcGhpYyBjaGFyYWN0ZXJpc3RpY3MsIHdlIGRldmVsb3BlZCBhIGNvbXBvc2l0ZSBtYXAgd2hlcmUgd2UgY2FuIHNlZSB3aGljaCBuZWlnaGJvcmhvb2RzIGhhdmUgZ2VudHJpZmljYXRpb24gaW5kaWNhdG9ycyBhbmQgYXJlIHNlZWluZyBhIGxvdCBvZiBuZXcgZGV2ZWxvcG1lbnQgZm9yIGEgZ2l2ZW4geWVhci4gKkNsaWNrIHRocm91Z2ggdGhlIHllYXJzIHRvIGxvb2sgYXQgaG93IGdlbnRyaWZpY2F0aW9uIGluZGljYXRvcnMgYW5kIHBlcm1pdHMgY2hhbmdlIG92ZXIgdGltZS4qCgpJbiAyMDIxLCB0aGVyZSBhcmUgYXJlYSB0aGF0IGFyZSBnZW50cmlmeWluZyBidXQgbm90IHNlZWluZyBhIGxvdCBvZiBuZXcgZGV2ZWxvcG1lbnQgLSBwYXJ0aWN1bGFybHkgaW4gdGhlIE5vcnRoZWFzdCBhbmQgTm9ydGh3ZXN0LiBQcmVkaWN0aW5nIGRldmVsb3BtZW50IGlzIHRoZXJlZm9yZSBnb2luZyB0byBiZSBzbGlnaHRseSBkaWZmZXJlbnQgdGhhbiBwcmVkaWN0aW5nIGNoYW5nZXMgaW4gcmVudHMsIGhvbWUgdmFsdWVzLCBhbmQgb3RoZXIgZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzIGxpa2UgcmFjaWFsIG1ha2V1cC4gCgpgYGB7cn0KYmlucyA9IGMoMCw1LDMwLDUwLDEwMCwyMDAsMzAwLEluZikKCnBlcm1pdHMxOCA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDE4KSAlPiUgLiRUT1RBTCkKCnBlcm1pdHMxOSA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDE5KSAlPiUgLiRUT1RBTCkKCnBlcm1pdHMyMCA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIwKSAlPiUgLiRUT1RBTCkKCnBlcm1pdHMyMSA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIxKSAlPiUgLiRUT1RBTCkKCnBlcm1pdHMyMiA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIyKSAlPiUgLiRUT1RBTCkKCnBlcm1pdHMyMyA8LSBjb2xvckJpbigKICBwYWxldHRlID0gInZpcmlkaXMiLAogIGJpbnMgPSBiaW5zLAogIGRvbWFpbiA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIzKSAlPiUgLiRUT1RBTCkKCmluZGljYXRvcl9wYWwgPC0gY29sb3JGYWN0b3IocGFsZXR0ZSA9IGMoIiNjMTkxZDkiLCAiIzZkNTQ4MCIsIiNmZjY2ZjAiLCAiI2ZjY2QzMiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgZG9tYWluID0gY2Vuc3VzX3Blcm1pdHMkaW5kaWNhdG9yKQoKcGVybWl0c19tYXAgPC0gbGVhZmxldCgpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuUG9zaXRyb24pICU+JQogIGFkZFBvbHlnb25zKGRhdGEgPSBjZW5zdXNfcGVybWl0cyAlPiUgZmlsdGVyKHllYXIgPT0gMjAyMyksCiAgICAgICAgICAgICAgc3Ryb2tlID0gVFJVRSwKICAgICAgICAgICAgICBjb2xvciA9IH5pbmRpY2F0b3JfcGFsKGluZGljYXRvciksCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwKICAgICAgICAgICAgICBvcGFjaXR5ID0gMSwKICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IC41LAogICAgICAgICAgICAgIGZpbGxDb2xvciA9IH5wZXJtaXRzMjMoVE9UQUwpLAogICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlRvdGFsIFBlcm1pdHM6IiwgY2Vuc3VzX3Blcm1pdHMgJT4lIGZpbHRlcih5ZWFyID09IDIwMjMpICU+JSAuJFRPVEFMKSwKICAgICAgICAgICAgICBncm91cCA9ICIyMDIzIikgJT4lCiAgICAgIGFkZFBvbHlnb25zKGRhdGEgPSBjZW5zdXNfcGVybWl0cyAlPiUgZmlsdGVyKHllYXIgPT0gMjAyMiksCiAgICAgICAgICAgICAgc3Ryb2tlID0gVFJVRSwKICAgICAgICAgICAgICBjb2xvciA9IH5pbmRpY2F0b3JfcGFsKGluZGljYXRvciksCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwKICAgICAgICAgICAgICBvcGFjaXR5ID0gMSwKICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IC41LAogICAgICAgICAgICAgIGZpbGxDb2xvciA9IH5wZXJtaXRzMjIoVE9UQUwpLAogICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlRvdGFsIFBlcm1pdHM6IiwgY2Vuc3VzX3Blcm1pdHMgJT4lIGZpbHRlcih5ZWFyID09IDIwMjIpICU+JSAuJFRPVEFMKSwKICAgICAgICAgICAgICBncm91cCA9ICIyMDIyIikgJT4lCiAgYWRkUG9seWdvbnMoZGF0YSA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIxKSwKICAgICAgICAgICAgICBzdHJva2UgPSBUUlVFLAogICAgICAgICAgICAgIGNvbG9yID0gfmluZGljYXRvcl9wYWwoaW5kaWNhdG9yKSwKICAgICAgICAgICAgICB3ZWlnaHQgPSAyLAogICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjUsCiAgICAgICAgICAgICAgZmlsbENvbG9yID0gfnBlcm1pdHMyMShUT1RBTCksCiAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiVG90YWwgUGVybWl0czoiLCBjZW5zdXNfcGVybWl0cyAlPiUgZmlsdGVyKHllYXIgPT0gMjAyMSkgJT4lIC4kVE9UQUwpLAogICAgICAgICAgICAgIGdyb3VwID0gIjIwMjEiKSAlPiUKICBhZGRQb2x5Z29ucyhkYXRhID0gY2Vuc3VzX3Blcm1pdHMgJT4lIGZpbHRlcih5ZWFyID09IDIwMjApLAogICAgICAgICAgICAgIHN0cm9rZSA9IFRSVUUsCiAgICAgICAgICAgICAgY29sb3IgPSB+aW5kaWNhdG9yX3BhbChpbmRpY2F0b3IpLAogICAgICAgICAgICAgIHdlaWdodCA9IDIsCiAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAuNSwKICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+cGVybWl0czIwKFRPVEFMKSwKICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJUb3RhbCBQZXJtaXRzOiIsIGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIwKSAlPiUgLiRUT1RBTCksCiAgICAgICAgICAgICAgZ3JvdXAgPSAiMjAyMCIpICU+JQogICAgYWRkUG9seWdvbnMoZGF0YSA9IGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDE5KSwKICAgICAgICAgICAgICBzdHJva2UgPSBUUlVFLAogICAgICAgICAgICAgIGNvbG9yID0gfmluZGljYXRvcl9wYWwoaW5kaWNhdG9yKSwKICAgICAgICAgICAgICB3ZWlnaHQgPSAyLAogICAgICAgICAgICAgIG9wYWNpdHkgPSAxLAogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gLjUsCiAgICAgICAgICAgICAgZmlsbENvbG9yID0gfnBlcm1pdHMxOShUT1RBTCksCiAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiVG90YWwgUGVybWl0czoiLCBjZW5zdXNfcGVybWl0cyAlPiUgZmlsdGVyKHllYXIgPT0gMjAxOSkgJT4lIC4kVE9UQUwpLAogICAgICAgICAgICAgIGdyb3VwID0gIjIwMTkiKSAlPiUKICBhZGRQb2x5Z29ucyhkYXRhID0gY2Vuc3VzX3Blcm1pdHMgJT4lIGZpbHRlcih5ZWFyID09IDIwMTgpLAogICAgICAgICAgICAgIHN0cm9rZSA9IFRSVUUsCiAgICAgICAgICAgICAgY29sb3IgPSB+aW5kaWNhdG9yX3BhbChpbmRpY2F0b3IpLAogICAgICAgICAgICAgIHdlaWdodCA9IDIsCiAgICAgICAgICAgICAgb3BhY2l0eSA9IDEsCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAuNSwKICAgICAgICAgICAgICBmaWxsQ29sb3IgPSB+cGVybWl0czE4KFRPVEFMKSwKICAgICAgICAgICAgICBncm91cCA9ICIyMDE4IiwKICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJUb3RhbCBQZXJtaXRzOiIsIGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDE4KSAlPiUgLiRUT1RBTCkpICU+JQogIGFkZExlZ2VuZChkYXRhID0gY2Vuc3VzX3Blcm1pdHMsCiAgICBwYWwgPSBpbmRpY2F0b3JfcGFsLAogICAgICAgICAgICB2YWx1ZSA9IH5pbmRpY2F0b3IsCiAgICB0aXRsZSA9ICJHZW50cmlmaWNhdGlvbiBJbmRpY2F0b3IiLAogICAgcG9zaXRpb24gPSAiYm90dG9tbGVmdCIpICU+JQogICBhZGRMYXllcnNDb250cm9sKAogICAgYmFzZUdyb3VwcyA9IGMoIjIwMjMiLCAiMjAyMiIsICIyMDIxIiwgIjIwMjAiLCAiMjAxOSIsICIyMDE4IiksCiAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpKSAKCnBlcm1pdHNfbWFwICU+JQogIHNldFZpZXcoIi03NS4xMzk0NDYiLCIzOS45OTY4NDkiLHpvb20gPSAxMSkKCmBgYAoKV2hlbiB3ZSBqdXN0IGxvb2sgYXQgb3VyIGdlbnRyaWZpY2F0aW9uIGluZGljYXRvciBhbG9uZSAod2l0aG91dCBwZXJtaXRzKSwgd2UgY2FuIHNlZSB0aGF0IG5vdCBhbGwgZGV2ZWxvcG1lbnQgaXMgYSByZXN1bHQgb2YgZ2VudHJpZmljYXRpb24sIGJ1dCB0aGVyZSBhcmUgc3RpbGwgc29tZSBhcmVhcyB3aGVyZSB0aGUgdHdvIG92ZXJsYXAuIEFsdGhvdWdoIHRoaXMgcHJvamVjdCBhaW1zIHRvIHByb3ZpZGUgdGFyZ2V0ZWQgYXJlYXMgZm9yIEhvdXNpbmcgVHJ1c3QgRnVuZHMsIHdlIGhhdmUgdG8gcmVjb2duaXplIHRoYXQgcGVybWl0cyBhbG9uZSBkbyBub3QgcGVyZmVjdGx5IGFsaWduIHdpdGggZ2VudHJpZmljYXRpb24sIGJ1dCBtZXJlbHkgcmVmbGVjdCBuZXcgZGV2ZWxvcG1lbnQuCgpgYGB7cn0KY2Vuc3VzX3Blcm1pdHMgJT4lCiAgZmlsdGVyKCFpcy5uYSh5ZWFyKSkgJT4lCiAgc3Rfc2YoKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX3NmKGFlcyhmaWxsID0gaW5kaWNhdG9yKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUxKSArCiAgbWFwVGhlbWUoKSArCiAgbGFicyh0aXRsZSA9ICJHZW50cmlmaWNhdGlvbiBJbmRpY2F0b3JzIiwgY2FwdGlvbiA9ICJGaWd1cmUgNyIpCmBgYAoKIyMgKipGRUFUVVJFIEVOR0lORUVSSU5HKioKCiMjIyBTcGF0aWFsIGFuZCBzcGF0aWFsIHRpbWUgbGFncywgQ2Vuc3VzIGRhdGEKCkRldmVsb3Bpbmcgc3BhdGlhbCBhbmQgc3BhdGlhbCB0aW1lIGxhZ3MgY2FuIGhlbHAgdXMgY2FwdHVyZSB0aGUgc3BhdGlhbCBlbmRvZ2VuZWl0eSBvZiBnZW50cmlmaWNhdGlvbiBhcyBkaXNjdXNzZWQgW2hlcmVdKGh0dHBzOi8vdXJiYW5zcGF0aWFsYW5hbHlzaXMuY29tL3BvcnRmb2xpby9wcmVkaWN0aW5nLWdlbnRyaWZpY2F0aW9uLXVzaW5nLWxvbmdpdHVkaW5hbC1jZW5zdXMtZGF0YS8jOn46dGV4dD1FbmRvZ2Vub3VzJTIwZ2VudHJpZmljYXRpb24mdGV4dD1CZWNhdXNlJTIwYXJlYXMlMjBpbiUyMGNsb3NlJTIwcHJveGltaXR5LGJ1dCUyMHdpdGglMjBsb3dlciUyMGhvdXNpbmclMjBjb3N0cykuIFdlIGNyZWF0ZWQgYSBzcGF0aWFsIHdlaWdodHMgbWF0cml4IGJhc2VkIG9uIGNvbnRpZ3VpdHkgcmVsYXRpb25zaGlwcyBvZiB0aGUgY2Vuc3VzIHRyYWN0cyBzaGFyaW5nIGVkZ2VzIGFuZCB2ZXJ0aWNlcy4gU3BlY2lmaWNhbGx5LCB3ZSBsb29rZWQgYXQgdGhlIG5lYXJlc3QgbmVpZ2hib3IgZm9yIHByaWNlcywgcmVudHMsIGFuZCBkZW1vZ3JhcGhpY3Mgb2YgbmVpZ2hib3JpbmcgY2Vuc3VzIHRyYWN0cyBhcyBwb3NzaWJsZSBrZXkgdmFyaWFibGVzIHRvIHByZWRpY3QgZm9yIHBlcm1pdCBkZXZlbG9wbWVudC4gCgpXZSBhbHNvIGNyZWF0ZWQgYSBsYWcgZm9yIHBlcm1pdHMgaXNzdWVkIHRvIGxhc3QgeWVhcidzIG5laWdoYm9ycy4gU3BhdGlhbCBsYWcgYW5kIHNwYXRpYWwgdGltZSBsYWcgaGFzIG1ham9yIHByZWRpY3RpdmUgcG90ZW50aWFsIGZvciBvdXIgbW9kZWxpbmcgZWZmb3J0cy4gVGhlIGF2ZXJhZ2UgcGVybWl0IGNvdW50IGZvciB0aGUgbmVhcmVzdCBjZW5zdXMgdHJhY3QgY2FuIGJlIGhlbHBmdWwsIGJ1dCB1dGlsaXppbmcgdGhlIG5lYXJlc3QgbmVpZ2hib3IgZm9yIHNvY2lhbCBkZW1vZ3JhcGhpYyBkYXRhIGhhcyB0aGUgcG90ZW50aWFsIHRvIGJlIG1vcmUgaW5mbHVlbmNlLiBBcyBoaWdoLWRlbWFuZCBuZWlnaGJvcmhvb2RzIGJlY29tZSBtb3JlIHByaWNlIHVuYXR0YWluYWJsZSwgcmVzaWRlbnRzIHByaWNlZCBvdXQgdHJ5IHRvIHJlbWFpbiBjbG9zZSBhbmQgY2hvb3NlIG5laWdoYm9yaG9vZHMgd2l0aCBzb21lIGFkamFjZW50IGNoYXJhY3RlcmlzdGljcyBzdWNoIGFzIGJhY2hlbG9ycyBkZWdlcmVlcywgaW5jb21lLCBvciB3aGl0ZSByZXNpZGVudHMsIGFuZCB0aGVyZWZvcmUgbW9yZSBsaWtlbGlob29kIG9mIGhpZ2hlciBzb2NpYWwgaW50ZWdyYXRpb24uIAoKYGBge3J9Cm5iIDwtIHN0X2NvbnRpZ3VpdHkoY2Vuc3VzX3Blcm1pdHMkZ2VvbWV0cnksIHF1ZWVuID0gVFJVRSkKd3QgPC0gc3Rfd2VpZ2h0cyhuYikKCmNlbnN1c19wZXJtaXRzIDwtIGNlbnN1c19wZXJtaXRzICU+JQogIG11dGF0ZShubl9tZWFuX3JlbnRfMjIgPSBzdF9sYWcoTWVkX1JlbnRfMjAyMixuYix3dCwgbmFfb2sgPSBUUlVFKSwKICAgICAgbm5fbWVhbl9wcmljZV8yMiA9IHN0X2xhZyhNZWRfVmFsdWVfMjAyMiwgbmIsd3QsIG5hX29rID0gVFJVRSksCiAgICAgIG5uX21lYW5fcmVudF8xNyA9IHN0X2xhZyhNZWRfUmVudF8yMDE3LG5iLHd0LCBuYV9vayA9IFRSVUUpLAogICAgICBubl9tZWFuX3ByaWNlXzE3ID0gc3RfbGFnKE1lZF9WYWx1ZV8yMDE3LCBuYix3dCwgbmFfb2sgPSBUUlVFKSwKICAgICAgbm5fYmFjaF8yMiA9IHN0X2xhZyhQZXJjZW50X2JhY2hfMjAyMiwgbmIsd3QsIG5hX29rID0gVFJVRSksCiAgICAgIG5uX2JhY2hfMTcgPSBzdF9sYWcoUGVyY2VudF9iYWNoXzIwMTcsIG5iLHd0LCBuYV9vayA9IFRSVUUpLAogICAgICBubl9pbmNvbWVfMjIgPSBzdF9sYWcoTWVkX0luY18yMDIyLCBuYix3dCwgbmFfb2sgPSBUUlVFKSwKICAgICAgbm5faW5jb21lXzE3ID0gc3RfbGFnKE1lZF9JbmNfMjAxNywgbmIsd3QsIG5hX29rID0gVFJVRSksCiAgICAgIG5uX3BjdF93aGl0ZV8yMiA9IHN0X2xhZyhQZXJjZW50X1doaXRlXzIwMjIsIG5iLHd0LCBuYV9vayA9IFRSVUUpLAogICAgICBubl9wY3Rfd2hpdGVfMTcgPSBzdF9sYWcoUGVyY2VudF9XaGl0ZV8yMDE3LCBuYix3dCwgbmFfb2sgPSBUUlVFKQogICAgICApCgpjZW5zdXNfcGVybWl0cyA8LSBjZW5zdXNfcGVybWl0cyAlPiUKICBncm91cF9ieShHRU9JRCkgJT4lCiAgYXJyYW5nZShHRU9JRCwgeWVhcikgJT4lCiAgbXV0YXRlKGxhc3RfeWVhcl9UT1RBTCA9IGRwbHlyOjpsYWcoVE9UQUwpLAogICAgICAgICBsYXN0X3llYXJfVE9UQUwgPSBpZmVsc2UoaXMubmEobGFzdF95ZWFyX1RPVEFMKSwgVE9UQUwsIGxhc3RfeWVhcl9UT1RBTCkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUobm5fTFlfcGVybWl0cyA9IHN0X2xhZyhsYXN0X3llYXJfVE9UQUwsIG5iLCB3dCkpCgpgYGAKCiMjIyBMb2NhbCBNb3JhbidzIEkKCk1vcmFuJ3MgSSBjYW4gaGVscCBpZGVudGlmeSBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiwgYW5kIHNpbWlsYXJpdHkgYW1vbmcgY2x1c3RlcnMgb2YgdmFsdWVzLiBNb3JhbidzIEkgdmFsdWVzIHRocm91Z2hvdXQgUGhpbGFkZWxwaGlhIGFyZSBsYXJnZWx5IGFib3ZlIDEsIGluZGljYXRpbmcgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBjbHVzdGVycyB3aGVyZWluIG5lYXJlc3QgbmVpZ2hib3JzIGFyZSBoYXZpbmcgYW4gaW1wYWN0IG9uIHBlcm1pdCBjb3VudC4gQmVsb3csIHdlIGNhbiBzZWUgdGhhdCB0aGF0IG1vc3Qgb2YgUGhpbGFkZWxwaGlhIGhhcyB2ZXJ5IGxvdyBNb3JhbidzIEkgdmFsdWVzIGV4Y2VwdCBmb3IgRmlzaHRvd24gYW5kIFVuaXZlcnNpdHkgQ2l0eSwgd2hpY2ggYXJlIG91dGxpZXJzLiBTaW1pbGFybHksIHRoZXJlIGFyZSBoaWdoZXIgcHJvYmFiaWxpdHkgdmFsdWVzIGluIHRoZSBhcmVhcyBzdXJyb3VuZGluZyBGaXNodG93biBhbmQgVW5pdmVyc2l0eSBDaXR5LCBwYXJ0aWN1bGFybHkgaW4gV2VzdCBQaGlsbHkuIAoKSGlnaGVyIHByb2JhYmlsaXR5IGZvciB0aGUgbmVhcmVzdCBuZWlnaGJvcmhvb2RzIHN1cnJvdW5kaW5nIHRoZSBoaWdoIGRlbWFuZCBuZWlnaGJvcmhvb2RzIHN1Z2dlc3QgdGhhdCB0aGVyZSBpcyBzb21lIHVuY2VydGFpbnR5IHJlZ2FyZGluZyBwcmVkaWN0aW9uIGZvciB0aGVzZSB0cmFjdHMuIEFsdGhvdWdoIHRoaXMgZ2VuZXJhbGx5IGluZGljYXRlcyB0aGF0IG91ciBjb25maWRlbmNlIGZvciBwcmVkaWN0aW5nIHRoZXNlIHRyYWN0cyBpcyBsb3dlciwgdGhlIGNsdXN0ZXJpbmcgb2YgdGhlc2UgcHJvYmFiaWxpdHkgdmFsdWVzIHN1Z2dlc3RzIHRoYXQgdGhlIG5lYXJlc3QgbmVpZ2hib3IgYW5kIHNwYXRpYWwgbGFnIGlzIGluZGljYXRpdmUgb2Ygc29tZSBmYWN0b3IsIGV2ZW4gaWYgd2UgYXJlIG5vdCBpZGVudGlmeWluZyBpdCB3aXRoIGV4YWN0bmVzcy4KCmBgYHtyfQoKbmIgPC0gcG9seTJuYihjZW5zdXNfcGVybWl0cyRnZW9tZXRyeSwgcXVlZW49VFJVRSkKd3QgPC0gbmIybGlzdHcobmIsIHN0eWxlPSJXIiwgemVyby5wb2xpY3k9VFJVRSkKCmxvY2FsX21vcmFucyA8LSBsb2NhbG1vcmFuKGNlbnN1c19wZXJtaXRzJFRPVEFMLCB3dCwgemVyby5wb2xpY3k9VFJVRSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHJlbmFtZShjKCJMb2NhbF9Nb3JhbnNfSSIgPSAiSWkiLCAiUF9WYWx1ZSIgPSAiUHIoeiAhPSBFKElpKSkiKSkKCmNlbnN1c19wZXJtaXRzIDwtIAogIGNiaW5kKGNlbnN1c19wZXJtaXRzLCBsb2NhbF9tb3JhbnMgJT4lIHNlbGVjdChMb2NhbF9Nb3JhbnNfSSwgUF9WYWx1ZSkpICU+JSAKICBzdF9zZigpICU+JQogIG11dGF0ZShTaWduaWZpY2FudF9Ib3RzcG90cyA9IGlmZWxzZShQX1ZhbHVlIDw9IDAuMDAxLCAxLCAwKSkgCgojUGxvdCBNb3JhbnMgSQoKbG9jYWxNb3JhbnMgPC0gCiAgY2JpbmQobG9jYWxfbW9yYW5zLCBjZW5zdXNfcGVybWl0cykgJT4lIAogIHN0X3NmKCkgJT4lCiAgZHBseXI6OnNlbGVjdChUT1RBTCwKICAgICAgICAgICAgICAgIExvY2FsX01vcmFuc19JLAogICAgICAgICAgICAgICAgUF9WYWx1ZSkgJT4lCiAgbXV0YXRlKFNpZ25pZmljYW50X0hvdHNwb3RzID0gaWZlbHNlKFBfVmFsdWUgPD0gMC4wMDEsIDEsIDApKSAlPiUKICBnYXRoZXIoVmFyaWFibGUsIFZhbHVlLCAtZ2VvbWV0cnkpCgojIyBUaGlzIGlzIGp1c3QgZm9yIHBsb3R0aW5nCnZhcnMgPC0gdW5pcXVlKGxvY2FsTW9yYW5zJFZhcmlhYmxlKQp2YXJMaXN0IDwtIGxpc3QoKQoKZm9yKGkgaW4gdmFycyl7CiAgdmFyTGlzdFtbaV1dIDwtIAogICAgZ2dwbG90KCkgKwogICAgICBnZW9tX3NmKGRhdGEgPSBmaWx0ZXIobG9jYWxNb3JhbnMsIFZhcmlhYmxlID09IGkpLCAKICAgICAgICAgICAgICBhZXMoZmlsbCA9IFZhbHVlKSwgY29sb3VyPU5BKSArCiAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19iKG5hbWU9IiIpICsKICAgICAgbGFicyh0aXRsZT1pKSArCiAgICAgIG1hcFRoZW1lKHRpdGxlX3NpemUgPSAxNCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpCn0KCgpkby5jYWxsKGdyaWQuYXJyYW5nZSxjKHZhckxpc3QsIG5jb2wgPSA0LCB0b3AgPSAiTG9jYWwgTW9yYW5zIEkgc3RhdGlzdGljcywgVG90YWwgUGVybWl0cyIpKQogIApgYGAKCiMjIyAqKkFkZGl0aW9uYWwgRmVhdHVyZSBFbmdpbmVlcmluZyoqCgojIyMjIENvZmZlZSBzaG9wcwoKRmluYWxseSwgd2UgZ2VuZXJhdGVkIGEgZmV3IGFtZW5pdHkgZmVhdHVyZXMgc3VjaCBhcyBzdG9yZXMgb3IgaW5mcmFzdHJ1Y3R1cmUgdGhhdCBtYXkgbWFrZSBhIG5laWdoYm9yaG9vZCBtb3JlIGF0dHJhY3RpdmUuIFRoZXNlIGluY2x1ZGUgY2FmZXMgZnJvbSAyMDE4IHRvIDIwMjMgc2luY2UgdGhlIHByZXNlbmNlIG9mIG5ldyBjYWZlcyBtYXkgYmUgY29ycmVsYXRlZCB3aXRoIGdlbnRyaWZpY2F0aW9uIG9yIG5ldyBkZXZlbG9wbWVudC4gCgpgYGB7ciByZXN1bHRzID0gJ2hpZGUnfQojY29mZmVlIHNob3BzIGJ5IHllYXIsIHRoZW4gam9pbiB0byBjZW5zdXNfcGVybWl0cy4KCiAjIGNhZmVzMTggPC0gZ2V0YmIoJ1BoaWxhZGVscGhpYSwgUEEnKSAlPiUKICMgICBvcHEoZGF0ZXRpbWUgPSAnMjAxOC0wMS0wMVQwMDowMDowMVonKSAlPiUKICMgICBhZGRfb3NtX2ZlYXR1cmUoa2V5ID0gImFtZW5pdHkiLCB2YWx1ZSA9ICJjYWZlIikgJT4lCiAjICAgb3NtZGF0YV9zZigpCiAjIAogIyBjYWZlczE4LnNmIDwtIHN0X2FzX3NmKGNhZmVzMTgkb3NtX3BvaW50cykKICMgCiAjIGNhZmVzMTguc2YgPC0gY2FmZXMxOC5zZiAlPiUKICMgICBzdF90cmFuc2Zvcm0oY3JzID0gc3RfY3JzKGNlbnN1c19wZXJtaXRzKSkgJT4lCiAjICAgc3Rfam9pbihjZW5zdXNfcGVybWl0cywgam9pbiA9IHN0X3dpdGhpbikKICMgCiAjIGNhZmVzMTguc2YgPC0gY2FmZXMxOC5zZiAlPiUKICMgICBmaWx0ZXIoIWlzLm5hKEdFT0lEKSkgJT4lCiAjICAgZ3JvdXBfYnkoR0VPSUQpICU+JQogIyAgIHN1bW1hcmlzZShjYWZlX2NvdW50ID0gbigpKSAlPiUKICMgICBtdXRhdGUoY2FmZXllYXIgPSAyMDE4KQogIyAKICMgY2FmZXMxOSA8LSBnZXRiYignUGhpbGFkZWxwaGlhLCBQQScpICU+JQogIyAgIG9wcShkYXRldGltZSA9ICcyMDE5LTAxLTAxVDAwOjAwOjAxWicpICU+JQogIyAgIGFkZF9vc21fZmVhdHVyZShrZXkgPSAiYW1lbml0eSIsIHZhbHVlID0gImNhZmUiKSAlPiUKICMgICBvc21kYXRhX3NmKCkKICMgCiAjIGNhZmVzMTkuc2YgPC0gc3RfYXNfc2YoY2FmZXMxOSRvc21fcG9pbnRzKSAlPiUKICMgICBzdF90cmFuc2Zvcm0oY3JzID0gc3RfY3JzKGNlbnN1c19wZXJtaXRzKSkgJT4lCiAjICAgICAgc3Rfam9pbihjZW5zdXNfcGVybWl0cywgam9pbiA9IHN0X3dpdGhpbikgJT4lCiAjICAgZmlsdGVyKCFpcy5uYShHRU9JRCkpICU+JQogIyAgIGdyb3VwX2J5KEdFT0lEKSAlPiUKICMgICBzdW1tYXJpc2UoY2FmZV9jb3VudCA9IG4oKSkgJT4lCiAjICAgbXV0YXRlKGNhZmV5ZWFyID0gMjAxOSkKICMgCiAjIGNhZmVzMjAgPC0gZ2V0YmIoJ1BoaWxhZGVscGhpYSwgUEEnKSAlPiUKICMgICBvcHEoZGF0ZXRpbWUgPSAnMjAyMC0wMS0wMVQwMDowMDowMVonKSAlPiUKICMgICBhZGRfb3NtX2ZlYXR1cmUoa2V5ID0gImFtZW5pdHkiLCB2YWx1ZSA9ICJjYWZlIikgJT4lCiAjICAgb3NtZGF0YV9zZigpCiAjIAogIyBjYWZlczIwLnNmIDwtIHN0X2FzX3NmKGNhZmVzMjAkb3NtX3BvaW50cykgJT4lCiAjICAgc3RfdHJhbnNmb3JtKGNycyA9IHN0X2NycyhjZW5zdXNfcGVybWl0cykpICU+JQogIyAgIHN0X2pvaW4oY2Vuc3VzX3Blcm1pdHMsIGpvaW4gPSBzdF93aXRoaW4pICU+JQogIyAgIGZpbHRlcighaXMubmEoR0VPSUQpKSAlPiUKICMgICBncm91cF9ieShHRU9JRCkgJT4lCiAjICAgc3VtbWFyaXNlKGNhZmVfY291bnQgPSBuKCkpICU+JQogIyAgIG11dGF0ZShjYWZleWVhciA9IDIwMjApCiAjIAogIyBjYWZlczIxIDwtIGdldGJiKCdQaGlsYWRlbHBoaWEsIFBBJykgJT4lCiAjICAgb3BxKGRhdGV0aW1lID0gJzIwMjEtMDEtMDFUMDA6MDA6MDFaJykgJT4lCiAjICAgYWRkX29zbV9mZWF0dXJlKGtleSA9ICJhbWVuaXR5IiwgdmFsdWUgPSAiY2FmZSIpICU+JQogIyAgIG9zbWRhdGFfc2YoKQogIyAKICMgIGNhZmVzMjEuc2YgPC0gc3RfYXNfc2YoY2FmZXMyMSRvc21fcG9pbnRzKSAlPiUKICMgICBzdF90cmFuc2Zvcm0oY3JzID0gc3RfY3JzKGNlbnN1c19wZXJtaXRzKSkgJT4lCiAjICAgc3Rfam9pbihjZW5zdXNfcGVybWl0cywgam9pbiA9IHN0X3dpdGhpbikgJT4lCiAjICAgZmlsdGVyKCFpcy5uYShHRU9JRCkpICU+JQogIyAgIGdyb3VwX2J5KEdFT0lEKSAlPiUKICMgICBzdW1tYXJpc2UoY2FmZV9jb3VudCA9IG4oKSkgJT4lCiAjICAgbXV0YXRlKGNhZmV5ZWFyID0gMjAyMSkKICMgCiAjIGNhZmVzMjIgPC0gZ2V0YmIoJ1BoaWxhZGVscGhpYSwgUEEnKSAlPiUKICMgICBvcHEoZGF0ZXRpbWUgPSAnMjAyMi0wMS0wMVQwMDowMDowMVonKSAlPiUKICMgICBhZGRfb3NtX2ZlYXR1cmUoa2V5ID0gImFtZW5pdHkiLCB2YWx1ZSA9ICJjYWZlIikgJT4lCiAjICAgb3NtZGF0YV9zZigpCiAjIAogIyBjYWZlczIyLnNmIDwtIHN0X2FzX3NmKGNhZmVzMjIkb3NtX3BvaW50cykgJT4lCiAjICAgc3RfdHJhbnNmb3JtKGNycyA9IHN0X2NycyhjZW5zdXNfcGVybWl0cykpICU+JQogIyAgIHN0X2pvaW4oY2Vuc3VzX3Blcm1pdHMsIGpvaW4gPSBzdF93aXRoaW4pICU+JQogIyAgIGZpbHRlcighaXMubmEoR0VPSUQpKSAlPiUKICMgICBncm91cF9ieShHRU9JRCkgJT4lCiAjICAgc3VtbWFyaXNlKGNhZmVfY291bnQgPSBuKCkpICU+JQogIyAgIG11dGF0ZShjYWZleWVhciA9IDIwMjIpCiAjIAogIyBjYWZlczIzIDwtIGdldGJiKCdQaGlsYWRlbHBoaWEsIFBBJykgJT4lCiAjICAgb3BxKGRhdGV0aW1lID0gJzIwMjMtMDEtMDFUMDA6MDA6MDFaJykgJT4lCiAjICAgYWRkX29zbV9mZWF0dXJlKGtleSA9ICJhbWVuaXR5IiwgdmFsdWUgPSAiY2FmZSIpICU+JQogIyAgIG9zbWRhdGFfc2YoKQogIyAKICMgY2FmZXMyMy5zZiA8LSBzdF9hc19zZihjYWZlczIzJG9zbV9wb2ludHMpICU+JQogIyAgIHN0X3RyYW5zZm9ybShjcnMgPSBzdF9jcnMoY2Vuc3VzX3Blcm1pdHMpKSAlPiUKICMgICBzdF9qb2luKGNlbnN1c19wZXJtaXRzLCBqb2luID0gc3Rfd2l0aGluKSAlPiUKICMgICBmaWx0ZXIoIWlzLm5hKEdFT0lEKSkgJT4lCiAjICAgZ3JvdXBfYnkoR0VPSUQpICU+JQogIyAgIHN1bW1hcmlzZShjYWZlX2NvdW50ID0gbigpKSAlPiUKICMgICBtdXRhdGUoY2FmZXllYXIgPSAyMDIzKQogIyAKICMgIGNhZmVzX2xpc3QgPC0gbGlzdChjYWZlczE4LnNmLCBjYWZlczE5LnNmLCBjYWZlczIwLnNmLCBjYWZlczIxLnNmLCBjYWZlczIyLnNmLAogIyAgICAgICAgICAgICAgICAgICAgIGNhZmVzMjMuc2YpCiAjIAogIyBhbGxfY2FmZXMgPC0gcmVkdWNlKGNhZmVzX2xpc3QsIGJpbmRfcm93cykgJT4lCiAjICAgcmVuYW1lKCJ5ZWFyIiA9ICJjYWZleWVhciIpICU+JQogIyAgc3RfZHJvcF9nZW9tZXRyeSgpCiAjIAogIyAKICMgd3JpdGUuY3N2KGFsbF9jYWZlcywgIkRhdGEvYWxsX2NhZmVzLmNzdiIpCiAjIAoKIyBybShjYWZlMTgsIGNhZmVzMTguc2YsIGNhZmVzMTksIGNhZmVzMTkuc2YsIGNhZmVzMjAsIGNhZmVzMjAuc2YsIGNhZmVzMjEsIGNhZmVzMjEuc2YsIGNhZmVzMjIsIGNhZmVzMjIuc2YsIGNhZmVzMjMsIGNhZmVzMjMuc2YpCgogYWxsX2NhZmVzIDwtIHJlYWQuY3N2KCJEYXRhL2FsbF9jYWZlcy5jc3YiKSAlPiUKICAgc2VsZWN0KC1YKQoKYWxsX2NhZmVzJEdFT0lEIDwtICBhcy5jaGFyYWN0ZXIoYWxsX2NhZmVzJEdFT0lEKQoKCiAgY2Vuc3VzX3Blcm1pdHMgPC0gY2Vuc3VzX3Blcm1pdHMgJT4lCiAgICBsZWZ0X2pvaW4oYWxsX2NhZmVzLCBieSA9IGMoIkdFT0lEIiwgInllYXIiKSkgJT4lCiAgICAgbXV0YXRlKGNhZmVfY291bnQgPSBpZmVsc2UoaXMubmEoY2FmZV9jb3VudCksIDAsIGNhZmVfY291bnQpKQoKYGBgCgojIyMjIERpc3RhbmNlIHRvIHRyYW5zaXQgc3RhdGlvbnMKCldlIGFsc28gbG9va2VkIGF0IHByb3hpbWl0eSB0byBmaXhlZCBwdWJsaWMgdHJhbnNwb3J0YXRpb24gd2l0aGluIHRoZSBjaXR5IG9mIFBoaWxhZGVscGhpYSwgaW5jbHVkaW5nIHJlZ2lvbmFsIHJhaWwsIHRoZSBCcm9hZCBTdHJlZXQgTGluZSwgTWFya2V0IEZyYW5rZm9yZCBMaW5lLCBhbmQgdHJvbGxleSBsaW5lcy4gCgpgYGB7ciByZXN1bHRzID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KcmVnaW9uYWxyYWlsIDwtIHN0X3JlYWQoIkRhdGEvUmVnaW9uYWxfUmFpbF9TdGF0aW9ucy5nZW9qc29uIikgCgp0cm9sbGV5cyA8LSBzdF9yZWFkKCJEYXRhL1Ryb2xsZXlfU3RhdGlvbnMuZ2VvanNvbiIpICU+JQogIHJlbmFtZShMaW5lX05hbWUgPSBMaW5lQWJiciwKICAgICAgICAgTGF0aXR1ZGUgPSBMYXQsCiAgICAgICAgIExvbmdpdHVkZSA9IExvbiwKICAgICAgICAgU3RhdGlvbl9OYSA9IFN0b3BOYW1lKSAlPiUKICBzZWxlY3QoRklELCBMaW5lX05hbWUsIFN0YXRpb25fTmEsIExhdGl0dWRlLCBMb25naXR1ZGUsIGdlb21ldHJ5KQogIAptZXRybyA8LSBzdF9yZWFkKCJEYXRhL0hpZ2hzcGVlZF9TdGF0aW9ucy5nZW9qc29uIikgJT4lCiAgcmVuYW1lKExpbmVfTmFtZSA9IFJvdXRlLCAKICAgICAgICAgU3RhdGlvbl9OYSA9IFN0YXRpb24pCgp0cmFuc2l0IDwtIHJiaW5kKHJlZ2lvbmFscmFpbCwgdHJvbGxleXMsIG1ldHJvKSAlPiUKICAgIHN0X3RyYW5zZm9ybShjcnMgPSBzdF9jcnMocGhsX3RyYWN0czIyKSkKCgpwaGxfdHJhbnNpdCA8LSBzdF9qb2luKHRyYW5zaXQsIHBobF90cmFjdHMyMiwgam9pbiA9IHN0X3dpdGhpbikgJT4lCiAgZmlsdGVyKCFpcy5uYShHRU9JRCkpICU+JQogIHNlbGVjdChMaW5lX05hbWUsIFN0YXRpb25fTmEsIExhdGl0dWRlLCBMb25naXR1ZGUsIEdFT0lELCBnZW9tZXRyeSkgJT4lCiAgc3RfdHJhbnNmb3JtKGNycyA9IHN0X2NycyhjZW5zdXNfcGVybWl0cykpCgpjZW5zdXNfcGVybWl0cyA8LSBjZW5zdXNfcGVybWl0cyAlPiUKICBtdXRhdGUobmVhcmVzdF90cmFuc2l0ID0gbm5fZnVuY3Rpb24oc3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQoY2Vuc3VzX3Blcm1pdHMpKSwgc3RfY29vcmRpbmF0ZXMocGhsX3RyYW5zaXQpLCBrPTEpKjY5LjIpCmBgYAoKIyMjIyBOdW1iZXIgb2Ygam9icyB3aXRoaW4gdHJhY3QKCkZpbmFsbHksIHdlIGxvb2tlZCBhdCBqb2IgY291bnRzIHBlciB0cmFjdC4gQWx0aG91Z2ggd2UgaW5pdGlhbGx5IHdlcmUgZ29pbmcgdG8gbG9vayBhdCBwbGFjZXMgc3VjaCBhcyBtYWpvciB1bml2ZXJzaXRpZXMgb3IgZGlzdGFuY2UgdG8gY2VudGVyIGNpdHksIHVsdGltYXRlbHksIHdlIHJlY29nbml6ZWQgdGhhdCB0aGVzZSBsb2NhdGlvbnMgYXJlIHN1YnN0aXR1dGVzIGZvciBqb2IgZGVuc2l0eS4gV2UgbG9va2VkIGF0IGpvYnMgb3ZlciB0aW1lLCBidXQgb3ZlcmFsbCB0aGUgcHJvcG9ydGlvbiByZW1haW5zIHByZXR0eSBzaW1pbGFyIGFjcm9zcyB0aGUgeWVhcnMgZm9yIHdoaWNoIHdlIGhhdmUgZGF0YS4gVGhlcmVmb3JlLCB3ZSBbaW50ZXJwb2xhdGVdKGh0dHBzOi8vd2Fsa2VyLWRhdGEuY29tL3RpZHljZW5zdXMvcmVmZXJlbmNlL2ludGVycG9sYXRlX3B3Lmh0bWwKaHR0cHM6Ly93YWxrZXItZGF0YS5jb20vdGlkeWNlbnN1cy9yZWZlcmVuY2UvaW50ZXJwb2xhdGVfcHcuaHRtbCkgdGhlIGdlb2dyYXBoaWVzIHRvIDIwMjAgdHJhY3RzIGFuZCB0aGVuIGxvb2sgYXQgc3BhdGlhbCBsYWcgLS0gam9icyBpbiBzdXJyb3VuZGluZyBjZW5zdXMgdHJhY3RzLiBUaGlzIGZlYXR1cmUgY291bGQgdXNlIHNvbWUgZnVydGhlciBkZXZlbG9wbWVudCwgYmVjYXVzZSBvdXIgdmlzdWFsIGFzc2Vzc21lbnQgaXMgdGhhdCBwZW9wbGUgZG8gbm90IHdhbnQgdG8gbGl2ZSBpbiB0aGUgYXJlYXMgd2l0aCB0aGUgaGlnaGVzdCBqb2IgZGVuc2l0eSwgYnV0IGEgd2l0aGluIGEgY2VydGFpbiBkaXN0YW5jZSBvZiB0aG9zZSBqb2IgY2VudGVycy4gCgpgYGB7cn0KI3VzZSBMZWhkciBwYWNrYWdlIHRvIGJyaW5nIGluIGpvYnMgYW5kIHRvIGFuYWx5emUgd2hlcmUgcGVvcGxlIGFyZSBnb2luZyBieSB3b3JrIGFuZCBob21lIGNlbnN1cyB0cmFjdC4gCiNkZXJpdmVkIGZyb206IGh0dHBzOi8vZ2l0aHViLmNvbS9qYW1ncmVlbi9sZWhkciAKCiNwYSBqb2JzIGZyb20gTEVIRFIKI21hdGNoIHdpdGggbG9kZXMgZGF0YSBmcm9tIHByZXZpb3VzIHllYXIsIGdyYWIgZGF0YSBmb3IgMjAxOSAtIDIwMjIKI21hdGNoIHRoZSBpbmRpdmlkdWFsIHBlcm1pdCBzaGVldCB3aXRoIHRoZSBwcmV2aW91cyB5ZWFycyBqb2IgZGF0YQojIAojIHBhX2pvYnMxOSA8LSBncmFiX2xvZGVzKHN0YXRlID0gInBhIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSAyMDE5LAojICAgICAgICAgICAgICAgICAgICAgICAgdmVyc2lvbiA9ICJMT0RFUzgiLAojICAgICAgICAgICAgICAgICAgICAgICAgbG9kZXNfdHlwZSA9ICJvZCIsCiMgICAgICAgICAgICAgICAgICAgICAgICBqb2JfdHlwZSA9ICJKVDAxIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIHNlZ21lbnQgPSAiUzAwMCIsCiMgICAgICAgICAgICAgICAgICAgICAgICBzdGF0ZV9wYXJ0ID0gIm1haW4iLAojICAgICAgICAgICAgICAgICAgICAgICAgYWdnX2dlbyA9ICJ0cmFjdCIpICU+JQojICAgc2VsZWN0KC1oX3RyYWN0KQojIAojIHBhX2pvYnMyMCA8LSBncmFiX2xvZGVzKHN0YXRlID0gInBhIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSAyMDIwLAojICAgICAgICAgICAgICAgICAgICAgICAgdmVyc2lvbiA9ICJMT0RFUzgiLAojICAgICAgICAgICAgICAgICAgICAgICAgbG9kZXNfdHlwZSA9ICJvZCIsCiMgICAgICAgICAgICAgICAgICAgICAgICBqb2JfdHlwZSA9ICJKVDAxIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIHNlZ21lbnQgPSAiUzAwMCIsCiMgICAgICAgICAgICAgICAgICAgICAgICBzdGF0ZV9wYXJ0ID0gIm1haW4iLAojICAgICAgICAgICAgICAgICAgICAgICAgYWdnX2dlbyA9ICJ0cmFjdCIpJT4lCiMgICBzZWxlY3QoLWhfdHJhY3QpCiMgCiMgcGFfam9iczIxIDwtIGdyYWJfbG9kZXMoc3RhdGUgPSAicGEiLAojICAgICAgICAgICAgICAgICAgICAgICAgeWVhciA9IDIwMjEsCiMgICAgICAgICAgICAgICAgICAgICAgICB2ZXJzaW9uID0gIkxPREVTOCIsCiMgICAgICAgICAgICAgICAgICAgICAgICBsb2Rlc190eXBlID0gIm9kIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIGpvYl90eXBlID0gIkpUMDEiLAojICAgICAgICAgICAgICAgICAgICAgICAgc2VnbWVudCA9ICJTMDAwIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIHN0YXRlX3BhcnQgPSAibWFpbiIsCiMgICAgICAgICAgICAgICAgICAgICAgICBhZ2dfZ2VvID0gInRyYWN0IiklPiUKIyAgIHNlbGVjdCgtaF90cmFjdCkKIyAKIyAjZmlsdGVyIGZvciBvbmx5IGpvYnMgdGhhdCBzdGFydCBvciBlbmQgaW4gUGhpbGFkZWxwaGlhIHRyYWN0cwojIAojIHBobF9qb2JzMTkgPC0gcGFfam9iczE5ICU+JQojICAgbGVmdF9qb2luKHBobF90cmFjdHMyMiwgYnkgPSBjKCJ3X3RyYWN0IiA9ICJHRU9JRCIpKSAlPiUKIyAgICAgZmlsdGVyKCFzdF9pc19lbXB0eShnZW9tZXRyeSkpICU+JQojICAgc3RfYXNfc2YoKSAlPiUKIyAgIHJlbmFtZShHRU9JRCA9IHdfdHJhY3QpICU+JQojICAgZ3JvdXBfYnkoR0VPSUQpICU+JQojICAgc3VtbWFyaXNlKGpvYl9jb3VudCA9IG4oKSkKIyAKIyBwaGxfam9iczE5IDwtIGludGVycG9sYXRlX3B3KAojICAgcGhsX2pvYnMxOSwKIyAgIHBobF90cmFjdHMyMiwKIyAgIHRvX2lkID0gIkdFT0lEIiwKIyAgIGV4dGVuc2l2ZSA9IFRSVUUsIAojICAgd2VpZ2h0cyA9IHBobF90cmFjdHMyMiwKIyAgIHdlaWdodF9jb2x1bW4gPSAiVG90YWxfUG9wIiwKIyAgIGNycyA9IDQyNjkpCiMgCiMgcGhsX2pvYnMyMCA8LSBwYV9qb2JzMjAgJT4lCiMgICBsZWZ0X2pvaW4ocGhsX3RyYWN0czIyLCBieSA9IGMoIndfdHJhY3QiID0gIkdFT0lEIikpICU+JQojICAgICBmaWx0ZXIoIXN0X2lzX2VtcHR5KGdlb21ldHJ5KSkgJT4lCiMgICBzdF9hc19zZigpICU+JQojICAgcmVuYW1lKEdFT0lEID0gd190cmFjdCkgJT4lCiMgICBncm91cF9ieShHRU9JRCkgJT4lCiMgICBzdW1tYXJpc2Uoam9iX2NvdW50ID0gbigpKQojIAojIHBobF9qb2JzMjEgPC0gcGFfam9iczIxICU+JQojICAgbGVmdF9qb2luKHBobF90cmFjdHMyMiwgYnkgPSBjKCJ3X3RyYWN0IiA9ICJHRU9JRCIpKSAlPiUKIyAgICAgZmlsdGVyKCFzdF9pc19lbXB0eShnZW9tZXRyeSkpICU+JQojICAgc3RfYXNfc2YoKSAlPiUKIyAgIHJlbmFtZShHRU9JRCA9IHdfdHJhY3QpICU+JQojICAgZ3JvdXBfYnkoR0VPSUQpICU+JQojICAgc3VtbWFyaXNlKGpvYl9jb3VudCA9IG4oKSkKIyAKIyBwaGxfam9ic19jb21iaW5lZCA8LSByYmluZChjYmluZChwaGxfam9iczE5LCB5ZWFyID0gMjAxOSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2JpbmQocGhsX2pvYnMyMCwgeWVhciA9IDIwMjApLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNiaW5kKHBobF9qb2JzMjEsIHllYXIgPSAyMDIxKSkKIyAKIyBnZ3Bsb3QocGhsX2pvYnNfY29tYmluZWQpICsKIyAgIGdlb21fc2YoYWVzKGZpbGwgPSBqb2JfY291bnQpKSArCiMgICBsYWJzKHRpdGxlID0gIkpvYiBEaXN0cmlidXRpb24gaW4gUGhpbGFkZWxwaGlhIiwgY29sb3IgPSAiSm9iIENvdW50IikgKwojICAgbWFwVGhlbWUoKSArCiMgICBmYWNldF93cmFwKH55ZWFyLCBuY29sID0gMykKIyAKIyBwaGxfam9ic19jb21iaW5lZCR5ZWFyIDwtIGFzLk51bWVyaWMocGhsX2pvYnNfY29tYmluZWQkeWVhcikKCnBobF9qb2JzX2NvbWJpbmVkIDwtIHJlYWQuY3N2KCJEYXRhL3BobF9qb2JzLmNzdiIpICU+JQogIHNlbGVjdCgtWCkgJT4lCiAgbXV0YXRlKEdFT0lEID0gYXMuY2hhcmFjdGVyKEdFT0lEKSkgJT4lCiAgcmVuYW1lKCJ5ZWFyX2pvYnMiID0gInllYXIiKQoKY2Vuc3VzX3Blcm1pdHMgPC0gY2Vuc3VzX3Blcm1pdHMgJT4lCiAgbXV0YXRlKHllYXJfam9icyA9IGNhc2Vfd2hlbih5ZWFyID09IDIwMTggfiAyMDE5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciA9PSAyMDIyIH4gMjAyMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyID09IDIwMjMgfiAyMDIxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgJWluJSBjKDIwMTksMjAyMCwyMDIxKSB+IHllYXIpKSAlPiUKICBsZWZ0X2pvaW4ocGhsX2pvYnNfY29tYmluZWQsIAogICAgICAgICAgICBieSA9IGMoIkdFT0lEIiwieWVhcl9qb2JzIikpICU+JQogIHNlbGVjdCgteWVhcl9qb2JzKQoKcGhsX2pvYnNfY29tYmluZWQgPC0gcGhsX2pvYnNfY29tYmluZWQgJT4lCiAgbGVmdF9qb2luKHBobF90cmFjdHMyMiwgYnkgPSBjKCJHRU9JRCIgPSAiR0VPSUQiKSkgJT4lCiAgc2VsZWN0KGpvYl9jb3VudCwgR0VPSUQsIGdlb21ldHJ5KSAlPiUKICBzdF9hc19zZigpCgpuYiA8LSBzdF9jb250aWd1aXR5KGNlbnN1c19wZXJtaXRzJGdlb21ldHJ5LCBxdWVlbiA9IFRSVUUpCnd0IDwtIHN0X3dlaWdodHMobmIpCgpjZW5zdXNfcGVybWl0cyA8LSBjZW5zdXNfcGVybWl0cyAlPiUKICAgbXV0YXRlKGpvYnMubm49IHN0X2xhZyhqb2JfY291bnQsbmIsd3QsIG5hX29rID0gVFJVRSkpCiAgCmBgYAoKIyMjIyBOZWlnaGJvcmhvb2RzCgpgYGB7ciByZXN1bHRzID0gJ2hpZGUnfQpjZW5zdXNfcGVybWl0cyA8LSBzdF9qb2luKHN0X2NlbnRyb2lkKGNlbnN1c19wZXJtaXRzKSwgcGhsLm5oLCBqb2luID0gc3Rfd2l0aGluKSAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgbGVmdF9qb2luKHBobF90cmFjdHM0MzI2KSAlPiUKICBzdF9hc19zZigpCmBgYAoKIyMjICoqRVhQTE9SQVRPUlkgQU5BTFlTSVMqKgoKSGVyZSwgd2UgbG9vayBhdCBvdXIgZGVwZW5kZW50IHZhcmlhYmxlLCB0b3RhbCBwZXJtaXRzLCBhcyBhIGZ1bmN0aW9uIG9mIG91ciBudW1lcmljIHZhcmlhYmxlcy4gTGFzdCB5ZWFyJ3MgcGVybWl0cyBhbmQgdGhlIExvY2FsIE1vcmFuJ3MgSSBhcmUgbW9zdCBzdHJvbmdseSBjb3JyZWxhdGVkLiAKClNvbWUgb2YgdGhlc2UgZ3JhcGhzIHNob3cgdGhhdCBwZXJtaXRzIHNwaWtlIGZvciB2YWx1ZXMgaW4gdGhlIG1pZGRsZSBvZiB0aGUgZGlzdHJpYnV0aW9uLiBBcyBhbiBleHBlcmltZW50LCB3ZSBjb3VsZCBtYWtlIGEgYmluYXJ5IHZhcmlhYmxlIGZvciByZWxhdGlvbnNoaXBzIHdoZXJlIHRoZXJlJ3MgYSBzcGlrZSBpbiB0aGUgbWlkZGxlIC0gYmV0d2VlbiB0aGUgNDV0aCBhbmQgNTV0aCBwZXJjZW50aWxlIG9yIG5vdC4gCgpgYGB7ciwgZmlnLmhlaWdodD0zMCwgZmlnLndpZHRoID0gMTB9CgojZmlsdGVyIHRoZSBvdXRsaWVycyAKY2Vuc3VzX3Blcm1pdHNfdGVzdCA8LSBjZW5zdXNfcGVybWl0cyAlPiUKICBmaWx0ZXIoVE9UQUwgPCA2MDApICU+JQogIGRwbHlyOjpzZWxlY3QoLWluZGljYXRvciwgLW1hcG5hbWUsIC1uYW1lLCAtU2lnbmlmaWNhbnRfSG90c3BvdHMpICU+JQogIGdhdGhlcih2YXJpYWJsZSwgdmFsdWUsIC1nZW9tZXRyeSwgLXllYXIsIC1HRU9JRCwgLVRPVEFMKQoKZ2dwbG90KGNlbnN1c19wZXJtaXRzX3Rlc3QsIGFlcyh2YWx1ZSwgVE9UQUwpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X3Ntb290aChhZXModmFsdWUsIFRPVEFMKSwgCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgc2l6ZSA9IDEsIGNvbG91cj0iI2ZmYjYwMCIpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzPSAiZnJlZSIsIG5jb2w9NCkgKwogIGxhYnModGl0bGUgPSAiRXhwbG9yYXRvcnkgQW5hbHlzaXMiLCBjYXB0aW9uID0gIkZpZ3VyZSA4IikKCmBgYAoKV2UgY2FuIHRlc3QgdGhpcyB0aGVvcnkgb3V0IG9uIHRoZSB2YXJpYWJsZXMgdGhhdCBhcHBlYXIgdG8gaGF2ZSB0aGUgbGFyZ2VzdCBzcGlrZXMgaW4gdGhlIG1pZGRsZS4gCkl0IHR1cm5zIG91dCB0aGlzIGlzIG9ubHkgdHJ1ZSBmb3IgdGhyZWUgdmFyaWFibGVzIC0tIHRoZSBtaWRkbGUgcGVyY2VudGlsZSAoYmV0d2VlbiA0NXRoIGFuZCA1NXRoIHBlcmNlbnRpbGUpIGlzIHJlbGF0ZWQgdG8gdGhlIG51bWJlciBvZiBwZXJtaXRzIGZvciB0aGUgY2hhbmdlIGluIHBlcmNlbnRhZ2Ugb2YgaG9tZXMgd2l0aCBhIHNlY29uZCBtb3J0Z2FnZSBiZXR3ZWVuIDIwMTcgYW5kIDIwMjIgNS1ZZWFyIEFDUyBzdXJ2ZXlzLCBmb3IgMjAxNyBhZGp1c3RlZCBtZWFuIHJlbnQsIGFuZCBmb3IgcGVyY2VudCBvd25lciBpbiAyMDIyLgoKYGBge3J9CmNlbnN1c19wZXJtaXRzX3Rlc3QgPC0gY2Vuc3VzX3Blcm1pdHMgJT4lCiAgbXV0YXRlKAogICAgbWlkZGxlX2JhY2hfMTcgPSBpZmVsc2UoQmFjaF8yMDE3ID4gcXVhbnRpbGUoQmFjaF8yMDE3LCAuNDUsIG5hLnJtID0gVFJVRSkgJiBCYWNoXzIwMTcgPD0gcXVhbnRpbGUoQmFjaF8yMDE3LCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgI21heWJlIHRoaXMgb25lCiAgICAgICAgIG1pZGRsZV9yZW50XzE3ID0gaWZlbHNlKE1lZF9SZW50XzIwMTcgPiBxdWFudGlsZShNZWRfUmVudF8yMDE3LCAuNDUsIG5hLnJtID0gVFJVRSkgJiBNZWRfUmVudF8yMDE3IDw9IHF1YW50aWxlKE1lZF9SZW50XzIwMTcsIC41NSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLAogICAgCiAgICAgICAgIG1pZGRsZV9wcmljZV9ORVQgPSBpZmVsc2UoTWVkX1ZhbHVlX05FVCA+IHF1YW50aWxlKE1lZF9WYWx1ZV9ORVQsIC40NSwgbmEucm0gPSBUUlVFKSAmIE1lZF9WYWx1ZV9ORVQgPD0gcXVhbnRpbGUoTWVkX1ZhbHVlX05FVCwgLjU1LCBuYS5ybSA9IFRSVUUpLCBUUlVFLCBGQUxTRSksCiAgICAKICAgICAgICAgbWlkZGxlX25uX2JhY2hfMjIgPSBpZmVsc2Uobm5fYmFjaF8yMiA+IHF1YW50aWxlKG5uX2JhY2hfMjIsIC40NSwgbmEucm0gPSBUUlVFKSAmIG5uX2JhY2hfMjIgPD0gcXVhbnRpbGUobm5fYmFjaF8yMiwgLjU1LCBuYS5ybSA9IFRSVUUpLCBUUlVFLCBGQUxTRSksCiAgICAKICAgICAgICAgbWlkZGxlX25uX2luY29tZV8yMiA9IGlmZWxzZShubl9pbmNvbWVfMjIgPiBxdWFudGlsZShubl9pbmNvbWVfMjIsIC40NSwgbmEucm0gPSBUUlVFKSAmIG5uX2luY29tZV8yMiA8PSBxdWFudGlsZShubl9pbmNvbWVfMjIsIC41NSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLAogICAgCiAgICAgICAgIG1pZGRsZV9ubl9tZWFuX3ByaWNlXzIyID0gaWZlbHNlKG5uX21lYW5fcHJpY2VfMjIgPiBxdWFudGlsZShubl9tZWFuX3ByaWNlXzIyLCAuNDUsIG5hLnJtID0gVFJVRSkgJiBubl9tZWFuX3ByaWNlXzIyIDw9IHF1YW50aWxlKG5uX21lYW5fcHJpY2VfMjIsIC41NSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLAogICAgCiAgICAgICAgIG1pZGRsZV9ubl9tZWFuX3JlbnRfMjIgPSBpZmVsc2Uobm5fbWVhbl9yZW50XzIyID4gcXVhbnRpbGUobm5fbWVhbl9yZW50XzIyLCAuNDUsIG5hLnJtID0gVFJVRSkgJiBubl9tZWFuX3JlbnRfMjIgPD0gcXVhbnRpbGUobm5fbWVhbl9yZW50XzIyLCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgICAgICBtaWRkbGVfT3duZXJzX1BDVCA9IGlmZWxzZShPd25lcnNfUENUID4gcXVhbnRpbGUoT3duZXJzX1BDVCwgLjQ1LCBuYS5ybSA9IFRSVUUpICYgT3duZXJzX1BDVCA8PSBxdWFudGlsZShPd25lcnNfUENULCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgI3RoaXMgb25lCiAgICAgICAgIG1pZGRsZV9QZXJjZW50XzJtb3J0X05FVCA9IGlmZWxzZShQZXJjZW50XzJtb3J0X05FVCA+IHF1YW50aWxlKFBlcmNlbnRfMm1vcnRfTkVULCAuNDUsIG5hLnJtID0gVFJVRSkgJiBQZXJjZW50XzJtb3J0X05FVCA8PSBxdWFudGlsZShQZXJjZW50XzJtb3J0X05FVCwgLjU1LCBuYS5ybSA9IFRSVUUpLCBUUlVFLCBGQUxTRSksCiAgICAKICAgICAgICAgbWlkZGxlX1BlcmNlbnRfYmFjaF8yMDIyID0gaWZlbHNlKFBlcmNlbnRfYmFjaF8yMDIyID4gcXVhbnRpbGUoUGVyY2VudF9iYWNoXzIwMjIsIC40NSwgbmEucm0gPSBUUlVFKSAmIFBlcmNlbnRfYmFjaF8yMDIyIDw9IHF1YW50aWxlKFBlcmNlbnRfYmFjaF8yMDIyLCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgICAgICBtaWRkbGVfUGVyY2VudF9iYWNoX1BDVCA9IGlmZWxzZShQZXJjZW50X2JhY2hfUENUID4gcXVhbnRpbGUoUGVyY2VudF9iYWNoX1BDVCwgLjQ1LCBuYS5ybSA9IFRSVUUpICYgUGVyY2VudF9iYWNoX1BDVCA8PSBxdWFudGlsZShQZXJjZW50X2JhY2hfUENULCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgI3RoaXMgb25lCiAgICAgICAgIG1pZGRsZV9QZXJjZW50X093bmVyXzIwMjIgPSBpZmVsc2UoUGVyY2VudF9Pd25lcl8yMDIyID4gcXVhbnRpbGUoUGVyY2VudF9Pd25lcl8yMDIyLCAuNDUsIG5hLnJtID0gVFJVRSkgJiBQZXJjZW50X093bmVyXzIwMjIgPD0gcXVhbnRpbGUoUGVyY2VudF9Pd25lcl8yMDIyLCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgICAgICBtaWRkbGVfUmVudF9JbmNvbWVfUmF0aW9fMjAxNyA9IGlmZWxzZShSZW50X0luY29tZV9SYXRpb18yMDE3ID4gcXVhbnRpbGUoUmVudF9JbmNvbWVfUmF0aW9fMjAxNywgLjQ1LCBuYS5ybSA9IFRSVUUpICYgUmVudF9JbmNvbWVfUmF0aW9fMjAxNyA8PSBxdWFudGlsZShSZW50X0luY29tZV9SYXRpb18yMDE3LCAuNTUsIG5hLnJtID0gVFJVRSksIFRSVUUsIEZBTFNFKSwKICAgIAogICAgI0Fsc28gdGVzdGluZyB0aGlzIG5ldyBiaW5hcnkgdmFyaWFibGUhCiAgICB0cmFuc2l0X3VuZGVyXzEuNSA9IGlmZWxzZShuZWFyZXN0X3RyYW5zaXQgPD0gMS41LCBUUlVFLCBGQUxTRSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUKICAKICBkcGx5cjo6c2VsZWN0KFRPVEFMLCBzdGFydHNfd2l0aCgnbWlkZGxlJyksIHRyYW5zaXRfdW5kZXJfMS41KSAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgZ2F0aGVyKHZhcmlhYmxlLCB2YWx1ZSwgLVRPVEFMKQoKY2Vuc3VzX3Blcm1pdHNfdGVzdCAlPiUKICBmaWx0ZXIoVE9UQUwgPCA2MDApICU+JQogIGdyb3VwX2J5KHZhcmlhYmxlLCB2YWx1ZSkgJT4lCiAgc3VtbWFyaXNlKG1lYW5fcGVybWl0cyA9IG1lYW4oVE9UQUwpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgICBnZ3Bsb3QoYWVzKHZhbHVlLG1lYW5fcGVybWl0cykpICsgCiAgICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdD0gImlkZW50aXR5IiwgZmlsbCA9ICIjZmZiNjAwIikgKyAKICAgICAgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIikgKwogICAgICBsYWJzKHg9IlRvdGFsIFBlcm1pdHMiLCB5PSJWYWx1ZSIsIAogICAgICAgICAgIHRpdGxlID0gIk1pZGRsZSBwZXJjZW50aWxlICg0NS01NXRoKSBhc3NvY2lhdGlvbnMgd2l0aCBudW1iZXIgb2YgcGVybWl0cyIsIGNhcHRpb24gPSAiRmlndXJlIDkiKSArCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgCgojIE11dGF0aW5nIG9ubHkgZm9yIHRoZSB2YXJpYWJsZXMgdmFyaWFibGVzIHRoYXQgbWF0dGVyZWQKCmNlbnN1c19wZXJtaXRzIDwtIGNlbnN1c19wZXJtaXRzICU+JQogIG11dGF0ZShtaWRkbGVfUGVyY2VudF9Pd25lcl8yMDIyID0gaWZlbHNlKFBlcmNlbnRfT3duZXJfMjAyMiA+IHF1YW50aWxlKFBlcmNlbnRfT3duZXJfMjAyMiwgLjQ1LCBuYS5ybSA9IFRSVUUpICYgUGVyY2VudF9Pd25lcl8yMDIyIDw9IHF1YW50aWxlKFBlcmNlbnRfT3duZXJfMjAyMiwgLjU1LCBuYS5ybSA9IFRSVUUpLCBUUlVFLCBGQUxTRSksCiAgICAgICAgIAogICAgICAgICBtaWRkbGVfUGVyY2VudF8ybW9ydF9ORVQgPSBpZmVsc2UoUGVyY2VudF8ybW9ydF9ORVQgPiBxdWFudGlsZShQZXJjZW50XzJtb3J0X05FVCwgLjQ1LCBuYS5ybSA9IFRSVUUpICYgUGVyY2VudF8ybW9ydF9ORVQgPD0gcXVhbnRpbGUoUGVyY2VudF8ybW9ydF9ORVQsIC41NSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLAogICAgICAgICAKICAgICAgICAgIG1pZGRsZV9yZW50XzE3ID0gaWZlbHNlKE1lZF9SZW50XzIwMTcgPiBxdWFudGlsZShNZWRfUmVudF8yMDE3LCAuNDUsIG5hLnJtID0gVFJVRSkgJiBNZWRfUmVudF8yMDE3IDw9IHF1YW50aWxlKE1lZF9SZW50XzIwMTcsIC41NSwgbmEucm0gPSBUUlVFKSwgVFJVRSwgRkFMU0UpLAogICAgICAgICAKICAgICAgICAgdHJhbnNpdF91bmRlcl8xLjUgPSBpZmVsc2UobmVhcmVzdF90cmFuc2l0IDw9IDEuNSwgVFJVRSwgRkFMU0UpKQpgYGAKCgpgYGB7cn0KI01hcHBpbmcgY2FmZXMgYW5kIHZhY2FuY3k6CgojIGdncGxvdChjZW5zdXNfcGVybWl0cykgKwojICAgZ2VvbV9zZihhZXMoZmlsbCA9IGNhZmVfY291bnQpKSArCiMgICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBDYWZlcyBwZXIgVHJhY3QiKSArCiMgICBmYWNldF93cmFwKH55ZWFyKSArCiMgICBtYXBUaGVtZSgpCiMgCiMgZ2dwbG90KGNlbnN1c19wZXJtaXRzICU+JSBmaWx0ZXIoeWVhciA9PSAyMDIzKSkgKwojICAgZ2VvbV9zZihhZXMoZmlsbCA9IFBlcmNlbnRfVmFjYW50XzIwMTYpKSArCiMgICBsYWJzKHRpdGxlID0gIlZhY2FuY3kgUmF0ZSAyMDE2IikgKwojICAgbWFwVGhlbWUoKQoKYGBgCgojIyMjIENvcnJlbGF0aW9uIE1hdHJpeAoKV2UgY29tcGFyZWQgdmFyaWFibGVzIGFnYWluc3QgZWFjaCBvdGhlciBpbiBhIGNvcnJlbGF0aW9uIHRlc3QuIE1hbnkgb2YgdGhlIHZhcmlhYmxlcyBjb3JyZWxhdGVkIHdpdGggZWFjaCBvdGhlciBtb3JlIHRoYW4gd2l0aCB0aGUgY291bnQgb2YgdG90YWwgcGVybWl0cy4gVGhlIG1vc3QgY29ycmVsYXRlZCB2YXJpYWJsZXMgd2l0aCBwZXJtaXQgY291bnQgYXJlIHZhY2FuY3kgd2l0aCBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uLCB3aGVyZWluIG1vcmUgdmFjYW5jeSBtZWFucyBoaWdoZXIgcGVybWl0IGlzc3VhbmNlLiBBZnRlciBzb21lIGFkZGl0aW9uYWwgYW5hbHlzaXMgYW5kIGZlYXR1cmUgZW5naW5lZXJpbmcgaG93ZXZlciwgd2UgcmVhbGl6ZSB0aGF0IGNsb3NlIHByb3hpbWl0eSB0byB0cmFuc2l0ICh3aXRoaW4gMS41IG1pbGUpIGlzIG1vcmUgc2lnbmlmaWNhbnQgdGhhbiB0aGUgbmVhcmVzdCBuZWlnaGJvciB0cmFuc2l0LiBWYXJpYWJsZXMgYXJlIG9mIGNvdXJzZSBjb3JyZWxhdGVkIHdpdGggZWFjaCBvdGhlciwgYnV0IG92ZXJhbGwgY2Vuc3VzIHZhcmlhYmxlcyBhcmUgbm90IGFzIGNvcnJlbGF0ZWQgd2l0aCBwZXJtaXQgY291bnQgYXMgZXhwZWN0ZWQuIAoKYGBge3J9CgpudW1lcmljVmFycyA8LSBjZW5zdXNfcGVybWl0cyAlPiUKICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lCiAgc2VsZWN0KFRPVEFMLCBjYWZlX2NvdW50LCBuZWFyZXN0X3RyYW5zaXQsIGpvYl9jb3VudCwgTWVkX0luY18yMDIyLCBSZW50X0luY29tZV9SYXRpb19ORVQsCiAgICAgICAgIFdoaXRlX1BvcF8yMDIyLCBNZWRfVmFsdWVfTkVULCBWYWNhbnRzXzIwMjIsIFZhY2FudHNfMjAxNywKICAgICAgICAgVmFjYW50c19QQ1QsIFZhY2FudHNfTkVUKSAKCnByaWNlLmNvcnIgPC0gY29yci50ZXN0KG51bWVyaWNWYXJzKQoKaWYgKHN1bShpcy5uYShudW1lcmljVmFycykpID4gMCkgewogICMgSGFuZGxlIG1pc3NpbmcgdmFsdWVzIChlLmcuLCBpbXB1dGUgb3IgcmVtb3ZlKQogIG51bWVyaWNWYXJzIDwtIG5hLm9taXQobnVtZXJpY1ZhcnMpICAjIFJlbW92ZSByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMKfQoKY29ycmVsYXRpb25fbWF0cml4IDwtIHJvdW5kKGNvcihudW1lcmljVmFycyksIDEpCnBfdmFsdWVzIDwtIGNvcl9wbWF0KG51bWVyaWNWYXJzKQoKZ2djb3JycGxvdCgKICBjb3JyZWxhdGlvbl9tYXRyaXgsCiAgcC5tYXQgPSBwX3ZhbHVlcywKICBjb2xvcnMgPSBjKCAiI2YyMDA4OSIsICJ3aGl0ZSIsICIjMmQwMGY3IiksCiAgdHlwZSA9ICJsb3dlciIsCiAgaW5zaWcgPSAiYmxhbmsiCiAgCikgKwpsYWJzKHRpdGxlID0gIkRlZ3JlZXMgb2YgQ29ycmVsYXRpb24gdG8gUGVybWl0cyBJc3N1ZWQiLCBjYXB0aW9uID0gIkZpZ3VyZSAxMCIpICsKICBhbm5vdGF0ZSgKICBnZW9tID0gInJlY3QiLAogIHhtaW4gPSAuNSwgeG1heCA9IDExLjUsIHltaW4gPSAuNSwgeW1heCA9IDEuNSwKICBmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSAiI2ZmYjYwMCIsIGFscGhhID0gMC41CiAgCgopCmBgYAoKIyMgKipNT0RFTElORyoqCgpGb3IgdGhlIGJhc2VsaW5lLCB3ZSBpbmNsdWRlZCBjZW5zdXMgdmFyaWFibGVzIGluY2x1ZGluZyBtZWRpYW4gSEggaW5jb21lIChDUEkgaW5mbGF0aW9uIGFkanVzdGVkKSwgTWVkaWFuIGhvbWUgdmFsdWUsIE1lZGlhbiByZW50LCBNZWRpYW4geWVhciBzdHJ1Y3R1cmUgYnVpbHQsIHRlbnVyZSwgbnVtYmVyIG9mIGhvdXNpbmcgdW5pdHMsIG51bWJlciBvZiB2YWNhbmNpZXMsIGNvdW50IG9mIHNlY29uZCBtb3J0Z2FnZXMsIHBlcmNlbnQgd2l0aCBhIEJhY2hlbG9yJ3MgZGVncmVlLCBhbmQgcGVyY2VudCB3aGl0ZSBwb3B1bGF0aW9uLiAKCkFmdGVyIGRvaW5nIG91ciBleHBsb3JhdG9yeSBhbmFseXNpcywgd2UgbmFycm93ZWQgZG93biB0aGUgdmFyaWFibGVzIGZvciBvdXIgbW9kZWwuIEZvciB0aGUgc3BhdGlhbCBhbmQgdGVtcG9yYWwgbGFnIG1vZGVsLCB3ZSB1c2VkIHRoZXNlIHZhcmlhYmxlcywgZ3JvdXBlZCBpbnRvIHRoZSBmb2xsb3dpbmcgY2F0ZWdvcmllczoKCioqVElNRS9TUEFDRSoqIDxicj4KLSBuYW1lIChuZWlnaGJvcmhvb2QpIDxicj4KLSB5ZWFyIDxicj4KCioqR0VOVFJJRklDQVRJT04gSU5ESUNBVE9SUyoqIDxicj4KLSBpbmRpY2F0b3IgKGluY29ycG9yYXRlcyBiYWNoZWxvcnMgZGVncmVlcywgaW5jb21lIHByaWNlIGNoYW5nZXMsIHJlbnQgY2hhbmdlcywgYW5kIHBvcHVsYXRpb24pIDxicj4KCioqREVNT0dSQVBISUMqKiAKKHRoZSBzdHJvbmdlc3QpIDxicj4KLSBCYWNoX05FVCA8YnI+Ci0gbWlkZGxlX1BlcmNlbnRfT3duZXJfMjAyMiA8YnI+Ci0gbWlkZGxlX1BlcmNlbnRfMm1vcnRfTkVUIDxicj4KCihvdGhlciBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMpCi0gTWVkX1ZhbHVlX05FVCA8YnI+Ci0gUGVyY2VudF8ybW9ydF8yMDIyIDxicj4KLSBQZXJjZW50X2JhY2hfTkVUIDxicj4KLSBSZW50X0luY29tZV9SYXRpb18yMDIyIDxicj4KLSBXaGl0ZV9Qb3BfTkVUIDxicj4KCioqQU1FTklUSUVTKiogPGJyPgotIGNhZmVfY291bnQgPGJyPgotIGpvYl9jb3VudCA8YnI+Ci0gdHJhbnNpdF91bmRlcl8xLjUgPGJyPgoKKipUSU1FIExBRyoqIDxicj4KLSBsYXN0X3llYXJfVE9UQUwgPGJyPgotIE1lZF9WYWx1ZV8yMDE3IDxicj4KLSBUb3RhbF9Vbml0c18yMDE3IDxicj4KLSBWYWNhbnRzXzIwMTcgPGJyPgotIG1pZGRsZV9yZW50XzE3IDxicj4KCioqU1BBVElBTCBMQUcqKiA8YnI+Ci0gTG9jYWwgTW9yYW4ncyBJIDxicj4KLSBubl9MWV9wZXJtaXRzIDxicj4KLSBubl9iYWNoXzIyIDxicj4KLSBubl9pbmNvbWVfMTcgPGJyPgoKU2V2ZXJhbCBpdGVyYXRpb25zIG9mIG1vZGVscyB3ZXJlIGRldmVsb3BlZCBvdmVyIHRoZSBjb3Vyc2Ugb2YgdGhpcyBwcm9qZWN0LCBlYWNoIGF0dGVtcHRpbmcgdG8gaXNvbGF0ZSBhbmQgY29hbGVzY2UgdmFyaWFibGVzIGluIGRpZmZlcmVudCBjb21iaW5hdGlvbnMgdG8gY3JlYXRlIGFzIGFjY3VyYXRlIGEgcHJlZGljdGlvbiBhcyBwb3NzaWJsZS4KCldlIHVzZWQgdGhyZWUgY3Jvc3MtdmFsaWRhdGlvbiBtZXRob2RzOiByYW5kb20gMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uLCBzcGF0aWFsIGNyb3NzLXZhbGlkYXRpb24sIGFuZCB0ZW1wb3JhbCBjcm9zcy12YWxpZGF0aW9uLiBCeSBjcm9zcyB2YWxpZGF0aW5nLCB3ZSBjYW4gZGV0ZWN0IGdlbmVyYWxpemFiaWxpdHkgJiBhY2N1cmFjeSBmb3IgdGhlIG1vZGVsJ3MgcHJlZGljdGlvbnMgYWNyb3NzIHRpbWUgYW5kIHNwYWNlIGluIFBoaWxhZGVscGhpYS4gQ3Jvc3MgdmFsaWRhdGlvbiB3aWxsIGFsc28gaGVscCBkZXRlcm1pbmUgd2hldGhlciBvdXIgbW9kZWwgaXMgb3ZlcmZpdHRpbmcuIFdpdGggb3ZlcnRmaXR0aW5nLCB0aGUgbW9kZWwgaXMgdmVyeSBhY2N1cmF0ZSBidXQgbm90IGdlbmVyYWxpemFibGUgdG8gbmV3IGRhdGFzZXRzIGFzIG5ldyBkYXRhIGJlY29tZXMgYXZhaWxhYmxlIGFuZCBpbnB1dCBpbnRvIHRoZSBtb2RlbC4gCgoKCmBgYHtyfQpzZXQuc2VlZCgxNTIpCgpjZW5zdXNfcGVybWl0c19kYXQgPC0gY2Vuc3VzX3Blcm1pdHMgJT4lCiAgZmlsdGVyKHllYXIgJWluJSBjKDIwMjAsIDIwMjEsIDIwMjIpKSAlPiUKICBtdXRhdGUoY3ZJRCA9IHNhbXBsZShyb3VuZChucm93KC4pIC8gMTE1LjUpLCAKICAgICAgICAgICAgICAgICAgICAgICBzaXplPW5yb3coLiksIHJlcGxhY2UgPSBUUlVFKSkgJT4lCiAgZmlsdGVyKG5hbWUgIT0gIkJZQkVSUlkiICYgbmFtZSAhPSAiV0lTU0FISUNLT05fUEFSSyIgJiBuYW1lICE9ICJHTEVOV09PRCIgJiBuYW1lICE9ICJXSVNURVIiCiAgICAgICAgICYgbmFtZSAhPSAiTUNHVUlSRSIpCmBgYAoKCmBgYHtyLCByZXN1bHRzID0gJ2hpZGUnLCBtZXNzYWdlID0gRkFMU0V9CgojIEJBU0VMSU5FCgpyZWcuYmFzZWxpbmUudmFycyA8LSBjKCJ5ZWFyIiwiaW5kaWNhdG9yIiwibmFtZSIsIlBlcmNlbnRfV2hpdGVfMjAyMiIsIlBlcmNlbnRfT3duZXJfMjAyMiIsICJQZXJjZW50X1ZhY2FudF8yMDIyIiwgIk1lZF9JbmNfMjAyMiIsIk1lZF9WYWx1ZV8yMDIyIiwiTWVkX1JlbnRfMjAyMiIsICJNZWRfU3RydWN0dXJlXzIwMjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVG90X1VuaXRzXzIwMjIiLCAiVmFjYW50c18yMDIyIiwgIlNlY19Nb3J0XzIwMjIiLCAiUGVyY2VudF9iYWNoXzIwMjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVudF9JbmNvbWVfUmF0aW9fMjAyMiIsCiAgICAgICAgICAgICAgICAgICAgICAgIkJhY2hfTkVUIiwgIm1pZGRsZV9QZXJjZW50X093bmVyXzIwMjIiLCJtaWRkbGVfUGVyY2VudF8ybW9ydF9ORVQiLAogICAgICAgICAgICAgICAgICAgICAgICJNZWRfVmFsdWVfTkVUIiwiUGVyY2VudF9iYWNoX05FVCIsICAiV2hpdGVfUG9wX05FVCIsCiAgICAgICAgICAgICAgICAgICAgICAiY2FmZV9jb3VudCIsICJqb2JfY291bnQiLCAidHJhbnNpdF91bmRlcl8xLjUiKQoKcmVnLmJhc2VsaW5lLnZhcnMuc3BhY2UgPC0gcmVnLmJhc2VsaW5lLnZhcnNbcmVnLmJhc2VsaW5lLnZhcnMgIT0gIm5hbWUiXQpyZWcuYmFzZWxpbmUudmFycy50aW1lIDwtIHJlZy5iYXNlbGluZS52YXJzW3JlZy5iYXNlbGluZS52YXJzICE9ICJ5ZWFyIl0KCiMgU1BBQ0UgQU5EIFRJTUUgLSB6ZXJvIGluZmxhdGVkCgpyZWcuc3QudmFycyA8LSBjKCAieWVhciIsICJpbmRpY2F0b3IiLCAibmFtZSIsIkJhY2hfTkVUIiwibWlkZGxlX1BlcmNlbnRfT3duZXJfMjAyMiIsIm1pZGRsZV9QZXJjZW50XzJtb3J0X05FVCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1lZF9WYWx1ZV9ORVQiLCAiUGVyY2VudF8ybW9ydF8yMDIyIiwgIlBlcmNlbnRfYmFjaF9ORVQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVudF9JbmNvbWVfUmF0aW9fMjAyMiIsICJXaGl0ZV9Qb3BfTkVUIiwgImNhZmVfY291bnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHJhbnNpdF91bmRlcl8xLjUiLCAgImxhc3RfeWVhcl9UT1RBTCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZWRfVmFsdWVfMjAxNyIsICJUb3RfVW5pdHNfMjAxNyIsICJWYWNhbnRzXzIwMTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibWlkZGxlX3JlbnRfMTciLCAgIkxvY2FsX01vcmFuc19JIiwgICJubl9MWV9wZXJtaXRzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5uX2JhY2hfMjIiLCAibm5faW5jb21lXzE3IiwgImpvYnMubm4iKQoKcmVnLnN0LnNwYWNlIDwtcmVnLnN0LnZhcnNbcmVnLnN0LnZhcnMgIT0gIm5hbWUiXSAKcmVnLnN0LnRpbWUgPC0gcmVnLnN0LnZhcnNbcmVnLnN0LnZhcnMgIT0gInllYXIiXQoKIyBCYXNlbGluZQpyZWcuYmFzZWxpbmUucmFuZG9tIDwtIGNyb3NzVmFsaWRhdGUoCiAgZGF0YXNldCA9IGNlbnN1c19wZXJtaXRzX2RhdCwKICBpZCA9ICJjdklEIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAKICBkZXBlbmRlbnRWYXJpYWJsZSA9ICJUT1RBTCIsCiAgaW5kVmFyaWFibGVzID0gcmVnLmJhc2VsaW5lLnZhcnMpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0gY3ZJRCwgVE9UQUwsIFByZWRpY3Rpb24sIG5hbWUpCgpyZWcuYmFzZWxpbmUuY3YudGltZSA8LSBjcm9zc1ZhbGlkYXRlKAogIGRhdGFzZXQgPSBjZW5zdXNfcGVybWl0c19kYXQsCiAgaWQgPSAieWVhciIsICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgZGVwZW5kZW50VmFyaWFibGUgPSAiVE9UQUwiLAogIGluZFZhcmlhYmxlcyA9IHJlZy5iYXNlbGluZS52YXJzLnRpbWUpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0geWVhciwgVE9UQUwsIFByZWRpY3Rpb24sIG5hbWUpCgpyZWcuYmFzZWxpbmUuY3Yuc3BhY2UgPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gY2Vuc3VzX3Blcm1pdHNfZGF0LAogIGlkID0gIm5hbWUiLCAgICAgICAgICAgICAgICAgICAgICAgICAgIAogIGRlcGVuZGVudFZhcmlhYmxlID0gIlRPVEFMIiwKICBpbmRWYXJpYWJsZXMgPSByZWcuYmFzZWxpbmUudmFycy5zcGFjZSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGN2SUQgPSBuYW1lLCBUT1RBTCwgUHJlZGljdGlvbikgJT4lCiAgbXV0YXRlKG5hbWUgPSBjdklEKSAlPiUKICBkcGx5cjo6c2VsZWN0KGN2SUQsIFRPVEFMLCBQcmVkaWN0aW9uLCBuYW1lLCBnZW9tZXRyeSkKCiMgc3BhY2UvdGltZSBsYWcKCnJlZy5zdC5jdi5yYW5kb20gPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gY2Vuc3VzX3Blcm1pdHNfZGF0LAogIGlkID0gImN2SUQiLCAgICAgICAgICAgICAgICAgICAgICAgICAgIAogIGRlcGVuZGVudFZhcmlhYmxlID0gIlRPVEFMIiwKICBpbmRWYXJpYWJsZXMgPSByZWcuc3QudmFycykgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGN2SUQgPSBjdklELCBUT1RBTCwgUHJlZGljdGlvbiwgbmFtZSkKCnJlZy5zdC5jdi50aW1lIDwtIGNyb3NzVmFsaWRhdGUoCiAgZGF0YXNldCA9IGNlbnN1c19wZXJtaXRzX2RhdCwKICBpZCA9ICJ5ZWFyIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAKICBkZXBlbmRlbnRWYXJpYWJsZSA9ICJUT1RBTCIsCiAgaW5kVmFyaWFibGVzID0gcmVnLnN0LnRpbWUpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0geWVhciwgVE9UQUwsIFByZWRpY3Rpb24sIG5hbWUpCgpyZWcuc3QuY3Yuc3BhY2UgPC0gY3Jvc3NWYWxpZGF0ZSgKICBkYXRhc2V0ID0gY2Vuc3VzX3Blcm1pdHNfZGF0LAogIGlkID0gIm5hbWUiLCAgICAgICAgICAgICAgICAgICAgICAgICAgIAogIGRlcGVuZGVudFZhcmlhYmxlID0gIlRPVEFMIiwKICBpbmRWYXJpYWJsZXMgPSByZWcuc3Quc3BhY2UpICU+JQogICAgZHBseXI6OnNlbGVjdChjdklEID0gbmFtZSwgVE9UQUwsIFByZWRpY3Rpb24pICU+JQogIG11dGF0ZShuYW1lID0gY3ZJRCkgJT4lCiAgZHBseXI6OnNlbGVjdChjdklELCBUT1RBTCwgUHJlZGljdGlvbiwgbmFtZSwgZ2VvbWV0cnkpCgpyZWcuc3VtbWFyeSA8LSAKICByYmluZCgKICAgIG11dGF0ZShyZWcuYmFzZWxpbmUucmFuZG9tLCAgICAgICAgICAgRXJyb3IgPSBQcmVkaWN0aW9uIC0gVE9UQUwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUmVncmVzc2lvbiA9ICJSYW5kb20gay1mb2xkIENWOiBCYXNlbGluZSBWYXJpYWJsZXMiKSwKICAgIAogICAgbXV0YXRlKHJlZy5zdC5jdi5yYW5kb20sICAgICAgICAgICBFcnJvciA9IFByZWRpY3Rpb24gLSBUT1RBTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdyZXNzaW9uID0gIlJhbmRvbSBrLWZvbGQgQ1Y6IFNwYXRpYWwgYW5kIFRlbXBvcmFsIExhZyIpLAogICAgCiAgICBtdXRhdGUocmVnLmJhc2VsaW5lLmN2LnNwYWNlLCAgICBFcnJvciA9IFByZWRpY3Rpb24gLSBUT1RBTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdyZXNzaW9uID0gIlNwYXRpYWwgTE9HTy1DVjogQmFzZWxpbmUgVmFyaWFibGVzIiksCiAgICAKICAgIG11dGF0ZShyZWcuc3QuY3Yuc3BhY2UsICAgIEVycm9yID0gUHJlZGljdGlvbiAtIFRPVEFMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZ3Jlc3Npb24gPSAiU3BhdGlhbCBMT0dPLUNWOiBTcGF0aWFsIGFuZCBUZW1wb3JhbCBMYWciKSwKICAgIAogICAgbXV0YXRlKHJlZy5iYXNlbGluZS5jdi50aW1lLCAgICBFcnJvciA9IFByZWRpY3Rpb24gLSBUT1RBTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSZWdyZXNzaW9uID0gIlRlbXBvcmFsIExPR08tQ1Y6IEJhc2VsaW5lIFZhcmlhYmxlcyIpLAogICAgCiAgICBtdXRhdGUocmVnLnN0LmN2LnRpbWUsICAgIEVycm9yID0gUHJlZGljdGlvbiAtIFRPVEFMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZ3Jlc3Npb24gPSAiVGVtcG9yYWwgTE9HTy1DVjogU3BhdGlhbCBhbmQgVGVtcG9yYWwgTGFnIikKICAgIAogICAgKSAlPiUKICAgIHN0X3NmKCkgCgpgYGAKCiMjIyBFeHBsb3JlIEVycm9ycwoKCk1lYW4gYWJzb2x1dGUgZXJyb3IgaXMgcXVpdGUgbG93IGFuZCBzaW1pbGFyIGFjcm9zcyBhbGwgY3Jvc3MgdmFsaWRhdGlvbiBtZXRob2RzIHdpdGggb25seSBhIGZldyBvdXRsaWVycy4gVGhlc2Ugb3V0bGllcnMgaGF2ZSBzaWduaWZpY2FudCBjb3VudCBkaWZmZXJlbmNlcywgYnV0IHRoZSByZXN0IG9mIHRoZSBuZWlnaGJvcmhvb2RzIGFyZSBvdmVyYWwgdGhlIHNhbWUuIFRoZSBzcGF0aWFsIGFuZCB0ZW1wb3JhbCBsYWcgbW9kZWwgd2hlbiBjcm9zcyB2YWxpZGF0ZWQgYXQgZmlyc3QgYXBwZWFycyB0byBiZSB0aGUgc3Ryb25nZXN0IG1vZGVsIHdpdGggdGhlIGxvd2VzdCBlcnJvciwgd2hpY2ggc3VnZ2VzdHMgdGhhdCB0aW1lIGFuZCBzcGFjZSBsYWcgZmFjdG9ycyBhcmUgdGhlIG1vc3QgaW5mbHVlbnRpYWwgcHJlZGljdG9ycy4gCgoKYGBge3J9CmVycm9yX2J5X3JlZyA8LSAKICByZWcuc3VtbWFyeSAlPiUKICAgIGdyb3VwX2J5KFJlZ3Jlc3Npb24pICU+JSAKICAgIHN1bW1hcml6ZShNZWFuX0Vycm9yID0gbWVhbihFcnJvciwgbmEucm0gPSBUKSwKICAgICAgICAgICAgICBNQUUgPSBtZWFuKGFicyhFcnJvciksIG5hLnJtID0gVCksCiAgICAgICAgICAgICAgU0RfTUFFID0gc2QoYWJzKEVycm9yKSwgbmEucm0gPSBUKSkgCgplcnJvcl9ieV9yZWdfbmggPC0gCiAgcmVnLnN1bW1hcnkgJT4lCiAgICBncm91cF9ieShSZWdyZXNzaW9uLCBuYW1lKSAlPiUgCiAgICBzdW1tYXJpemUoTWVhbl9FcnJvciA9IG1lYW4oUHJlZGljdGlvbiAtIFRPVEFMLCBuYS5ybSA9IFQpLAogICAgICAgICAgICAgIE1BRSA9IG1lYW4oYWJzKFByZWRpY3Rpb24gLSBUT1RBTCksIG5hLnJtID0gVCksCiAgICAgICAgICAgICAgU0RfTUFFID0gc2QoYWJzKFByZWRpY3Rpb24gLSBUT1RBTCksIG5hLnJtID0gVCkpICU+JQogIHVuZ3JvdXAoKQoKIyMgcGxvdCBoaXN0b2dyYW0gb2YgZXJyb3JzCmVycm9yX2J5X3JlZ19uaCAlPiUKICBnZ3Bsb3QoYWVzKE1BRSkpICsgCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGNvbG91cj0iYmxhY2siLCBmaWxsID0gIiNmMjAwODkiKSArCiAgZmFjZXRfd3JhcCh+UmVncmVzc2lvbiwgbmNvbCA9IDIpICsKICAgIGxhYnModGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiBNQUUgYnkgUmVncmVzc2lvbiBhbmQgTmVpZ2hib3Job29kIiwKICAgICAgICAgeD0iTWVhbiBBYnNvbHV0ZSBFcnJvciIsIHk9IkNvdW50IikKCgpgYGAKCiMjIyMgU3BhdGlhbCBEaXN0cmlidXRpb24gb2YgRXJyb3JzCgpXaGVuIHdlIGV4YW1pbmUgdGhlIHRlbXBvcmFsIGFuZCBzcGF0aWFsIGxhZyBpbiBmdXJ0aGVyIGRldGFpbCwgaXQgYmVjb21lcyBhcHBhcmVudCB0aGF0IHRoZSBuZWlnaGJvcmhvb2RzIHdpdGggaGlnaGVzdCBvdXRsaWVyIGluIHByZWRpY3RlZCBjb3VudCBhcmUgbmVhciB0aGUgRmlzaHRvd24gYW5kIFNvdXRod2VzdCBQaGlsbHkgbmVpZ2hib3Job29kcy4gT3RoZXIgdGhhbiB0aGVzZSwgZXJyb3IgaXMgcXVpdGUgc21hbGwgdGhyb3VnaG91dCB0aGUgcmVzdCBvZiBQaGlsYWRlbHBoaWEuIAoKYGBge3J9CgpyZWcuc3VtbWFyeSAlPiUKICBmaWx0ZXIoUmVncmVzc2lvbiA9PSAiVGVtcG9yYWwgTE9HTy1DVjogU3BhdGlhbCBhbmQgVGVtcG9yYWwgTGFnIikgJT4lCiAgZ3JvdXBfYnkobmFtZSkgJT4lCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQogIHN1bW1hcml6ZShyZXNpZHVhbHMgPSBtZWFuKEVycm9yLCBuYS5ybSA9IFQpKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIGxlZnRfam9pbihwaGwubmgsIGJ5ID0gYygibmFtZSIgPSAibmFtZSIpKSAlPiUKICAgIHN0X3NmKCkgJT4lCiAgICBnZ3Bsb3QoKSArIAogICAgICBnZW9tX3NmKGFlcyhmaWxsID0gcmVzaWR1YWxzKSkgKwogICAgICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiIzg5MDBmMiIgLCBtaWQgPSAgIiNmZmI2MDAiLCBoaWdoID0gIiNlNTAwYTQiLCBuYW1lID0gIkVycm9yIikgKwogICAgICBsYWJzKHRpdGxlID0gIlNwYXRpYWwgYW5kIFRlbXBvcmFsIExhZzogXG5NZWFuIEFic29sdXRlIEVycm9yIGJ5IE5laWdoYm9yaG9vZCIsIGNhcHRpb24gPSAiRmlndXJlIDEwIikgKwogICAgICBtYXBUaGVtZSgpCgoKYGBgCgpGaW5hbGx5LCB3aGVuIHBsb3R0aW5nIHByZWRpY3RlZCBwZXJtaXRzIGNvbXBhcmVkIHdpdGggb2JzZXJ2ZWQgcGVybWl0cywgd2UgY2FuIHNlZSB0aGF0IHRoZSBvYnNlcnZlZCBhbmQgcHJlZGljdGVkIHBlcm1pdHMgYXJlIGZhaXJseSBjbG9zZSBidXQgbm90IHNvIG11Y2ggdGhhdCB0aGUgbW9kZWwgaXMgb3ZlcmZpdHRpbmcuIEl0IGlzIHRoZXJlZm9yZSBnZW5lcmFsaXphYmxlIGFuZCBvdmVyYWxsIGFjY3VyYXRlLiAKCmBgYHtyfQoKcmVnLnN1bW1hcnkgJT4lCiAgZmlsdGVyKFJlZ3Jlc3Npb24gPT0gIlRlbXBvcmFsIExPR08tQ1Y6IFNwYXRpYWwgYW5kIFRlbXBvcmFsIExhZyIpICU+JSAgCiAgZ2dwbG90KGFlcyhUT1RBTCwgUHJlZGljdGlvbikpICsKICBnZW9tX3BvaW50KCkgKwogIHN0YXRfc21vb3RoKGFlcyhUT1RBTCwgVE9UQUwpLCAKICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIHNpemUgPSAxLCBjb2xvdXI9IiNmZmI2MDAiKSArIAogIHN0YXRfc21vb3RoKGFlcyhQcmVkaWN0aW9uLCBUT1RBTCksIAogICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIHNpemUgPSAxLCBjb2xvdXI9IiNmMjAwODkiKSArCiAgbGFicyh0aXRsZT0iUHJlZGljdGVkIHBlcm1pdHMgYXMgYSBmdW5jdGlvbiBvZiBvYnNlcnZlZCBwZXJtaXRzIiwKICAgICAgIHN1YnRpdGxlPSJPcmFuZ2UgbGluZSByZXByZXNlbnRzIGEgcGVyZmVjdCBwcmVkaWN0aW9uOyBQaW5rIGxpbmUgcmVwcmVzZW50cyBwcmVkaWN0aW9uIiwgY2FwdGlvbiA9ICJGaWd1cmUgMjEiKSArCiAgeGxhYigiQWN0dWFsIFBlcm1pdHMiKSArCiAgeWxhYigiUHJlZGljdGVkIFBlcm1pdHMiKSArCiAgcGxvdFRoZW1lKCkKCgoKYGBgCgpPdXIgc3BhdGlhbCByZWdyZXNzaW9uIG1vZGVsIGNyb3NzIHZhbGlkYXRlcyBvbiBhIG5laWdoYm9yaG9vZCBsZXZlbCBhY3Jvc3MgdGltZSwgd2hpbGUgb3VyIHRlbXBvcmFsIHJlZ3Jlc3Npb24gY3Jvc3MgdmFsaWRhdGVzIHllYXIgYnkgeWVhci4gUmVncmVzc2lvbiBlcnJvciBmb3IgdGhlIHRlbXBvcmFsIGxvZ28gY3Jvc3MgdmFsaWRhdGlvbiB3aXRoIHNwYXRpYWwgYW5kIHRlbXBvcmFsIGxhZyBoYXZlIHRoZSBsb3dlc3QgbWVhbiBhYnNvbHV0ZSBlcnJvciBhcyB3ZWxsIGFzIHN0YW5kYXJkIGRldmlhdGlvbiwgd2hpY2ggcmVtYWlucyBjb25zaXN0ZW50IHdpdGggb3VyIG90aGVyIGZpbmRpbmdzIG9mIHRoYXQgbW9kZWwuIEl0IGlzIGNsZWFyIHRoYXQgdGhpcyBtb2RlbCBpcyBnZW5lcmFsaXphYmxlLCBhY2N1cmF0ZSwgYW5kIG92ZXJhbGwgYSBnb29kIGZpdCBhY3Jvc3MgbWVhc3VyZW1lbnRzLiBCZWNhdXNlIGNyb3NzIHZhbGlkYXRpbmcgb24gYSB0ZW1wb3JhbCBzY2FsZSBhcHBlYXJzIHRvIGhhdmUgdGhlIGxvd2VzdCBtZWFuIE1BRSBhbmQgU3RhbmRhcmQgRGV2aWF0aW9uLCB3ZSBjYW4gYmUgZmFpcmx5IGNlcnRhaW4gdGhhdCB0aW1lIGlzIGtleSB0byBwcmVkaWN0aW5nIHBlcm1pdCBpc3N1YW5jZS4gVGhpcyBpcyBhbHNvIGltcG9ydGFudCBzaW5jZSBpdCBtZWFucyB0aGF0IGFkZGl0aW9uYWwgeWVhcnMgb2YgZGF0YSB3aWxsIGlkZWFsbHkgYmUgYWJsZSB0byBwcmVkaWN0IHBlcm1pdCBpc3N1YW5jZSB3aXRoIGZ1dHVyZSB5ZWFycyBvZiBkYXRhc2V0cy4gCgoKYGBge3J9CnN0X2Ryb3BfZ2VvbWV0cnkoZXJyb3JfYnlfcmVnX25oKSAlPiUKICBncm91cF9ieShSZWdyZXNzaW9uKSAlPiUgCiAgICBzdW1tYXJpemUoTWVhbl9NQUUgPSByb3VuZChtZWFuKE1BRSksIDIpLAogICAgICAgICAgICAgIFNEX01BRSA9IHJvdW5kKHNkKE1BRSksIDIpKSAlPiUKICBrYWJsZSgpICU+JQogICAga2FibGVfc3R5bGluZygic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGKSAlPiUKICAgIHJvd19zcGVjKDMsIGNvbG9yID0gImJsYWNrIiwgYmFja2dyb3VuZCA9ICIjODkwMGYyIikgJT4lCiAgICByb3dfc3BlYyg0LCBjb2xvciA9ICJibGFjayIsIGJhY2tncm91bmQgPSAiIzg5MDBmMiIpICU+JQogICAgcm93X3NwZWMoNSwgY29sb3IgPSAiYmxhY2siLCBiYWNrZ3JvdW5kID0gIiNmZmI2MDAiKSAlPiUKICAgIHJvd19zcGVjKDYsIGNvbG9yID0gImJsYWNrIiwgYmFja2dyb3VuZCA9ICIjZmZiNjAwIikKCgoKYGBgCgojIyAqKkNPTkNMVVNJT04qKgoKIyMjIERpc2N1c3Npb24KCk92ZXJhbGwsIHRoaXMgbW9kZWwgaXMgZWZmZWN0aXZlIGFuZCB3aGVuIGNvdXBsZWQgd2l0aCBpbmRpY2F0b3JzIG9mIGdlbnRyaWZpY2F0aW9uLCBjYW4gaGVscCBwcmVkaWN0IHdoaWNoIG5laWdoYm9yaG9vZHMgYXJlIG1vc3QgbGlrZWx5IGFyZWFzIHdpdGggaGlnaCBhbW91bnRzIG9mIGRldmVsb3BtZW50IGFuZCBleHBlcmllbmNpbmcgaW50ZW5zZSBuZWlnaGJvcmhvb2QgY2hhbmdlLiBUbyBwcmV2ZW50IGRpc3BsYWNlbWVudCBmb3IgbG9uZy10ZXJtIGFuZCBzb2Npb2Vjb25vbWljYWxseSBkaXNhZHZhbnRhZ2VkIHJlc2lkZW50cywgY29tbXVuaXR5IGxhbmQgdHJ1c3RzIGFuZCBob3VzaW5nIHRydXN0IGZ1bmRzIGNhbiBmdW5kIGxvbmctdGVybSBzb2x1dGlvbnMuIFNpbmNlIGl0IGlzIHRpbWUtY29uc3VtaW5nIHRvIGRldmVsb3AsICoqZnVuZCoqLCBhbmQgaW1wbGVtZW50IHRoZXNlIHR5cGVzIG9mIHNvbHV0aW9ucywgYW50aWNpcGF0aW5nIGEgZmV3IHllYXJzIG91dCBjYW4gcHJldmVudCBkaXNwbGFjZW1lbnQgYW5kIGNyZWF0ZSBsb25nLXRlcm0gYWZmb3JkYWJsZSBob3VzaW5nLiBUYXJnZXRpbmcgYXJlYXMgdGhhdCBhcmUgZXhwZXJpZW5jaW5nIGVhcmx5IGluZGljYXRvcnMgb2YgZ2VudHJpZmljYXRpb24gYnV0IG5vdCBoaWdoIGFtb3VudHMgb2YgcGVybWl0IGRldmVsb3BtZW50IGlzIGEgY3JpdGljYWwgc3RyYXRlZ3kuIAoKIyMjIFVzZSBDYXNlIEFwcGxpY2F0aW9uCgoKSWRlbnRpZnlpbmcgYXJhcyBmb3IgY29tbXVuaXR5IGxhbmQgdHJ1c3QgaW52ZXN0bWVudCBjYW4gaGVscCBhcyBhIGxvbmcgdGVybSBzdHJhdGVneSBmb3IgYW50aS1kaXNwbGFjZW1lbnQsIGxpa2Ugem9uaW5nLiBbQ29tbXVuaXR5IGxhbmQgdHJ1c3RzXShodHRwczovL2dyb3VuZGVkc29sdXRpb25zLm9yZy9zdHJlbmd0aGVuaW5nLW5laWdoYm9yaG9vZHMvY29tbXVuaXR5LWxhbmQtdHJ1c3RzKSB3aWxsIGJlIGluZmx1ZW50aWFsIGluIHByZXZlbnRpbmcgUGhpbGFkZWxwaGlhIGZyb20gcHVzaGluZyBvdXQgaXRzIGxvbmctdGVybSByZXNpZGVudHMuIApJZGVudGlmeWluZyBhcmVhcyBmb3IgY29tbXVuaXR5IGxhbmQgdHJ1c3QgaW52ZXN0bWVudCBjYW4gaGVscCBhcyBhIGxvbmcgdGVybSBzdHJhdGVneSB0byBmaWdodCBkaXNwbGFjZW1lbnQuIFdlIGhvcGUgdGhhdCB0aGlzIGNhbiBoZWxwIHNlY3VyZSBhZmZvcmRhYmxlIGhvdXNpbmcgYW5kIHByZWRpY3QgdGhlIGJlc3QgYXJlYXMgdG8gc2VjdXJlIGl0IGZvciB0aGUgZnV0dXJlLiAKCkZvciBhIGJyaWVmIHByZXNlbnRhdGlvbiBvZiB0aGlzIG1hcmtkb3duLCBwbGVhc2UgdmlzaXQgb3VyIFt2aWRlbyBoZXJlXSgpCg==